libnl 2.0
|
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-2008 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_alloc(q); 00066 if (!tbf) 00067 return -NLE_NOMEM; 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 void tbf_free_data(struct rtnl_qdisc *qdisc) 00097 { 00098 free(qdisc->q_subdata); 00099 } 00100 00101 static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) 00102 { 00103 double r, rbit, lim; 00104 char *ru, *rubit, *limu; 00105 struct rtnl_tbf *tbf = tbf_qdisc(qdisc); 00106 00107 if (!tbf) 00108 return; 00109 00110 r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru); 00111 rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit); 00112 lim = nl_cancel_down_bytes(tbf->qt_limit, &limu); 00113 00114 nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", 00115 r, ru, rbit, rubit, lim, limu); 00116 } 00117 00118 static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) 00119 { 00120 struct rtnl_tbf *tbf = tbf_qdisc(qdisc); 00121 00122 if (!tbf) 00123 return; 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 nl_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 nl_dump_line(p, " 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 00154 static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc) 00155 { 00156 struct tc_tbf_qopt opts; 00157 struct rtnl_tbf *tbf; 00158 struct nl_msg *msg; 00159 uint32_t rtab[RTNL_TC_RTABLE_SIZE]; 00160 uint32_t ptab[RTNL_TC_RTABLE_SIZE]; 00161 int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT; 00162 00163 memset(&opts, 0, sizeof(opts)); 00164 00165 tbf = tbf_qdisc(qdisc); 00166 if (!tbf) 00167 return NULL; 00168 00169 if (!(tbf->qt_mask & required) != required) 00170 return NULL; 00171 00172 opts.limit = tbf->qt_limit; 00173 opts.buffer = tbf->qt_rate_txtime; 00174 tbf->qt_rate.rs_mpu = tbf->qt_mpu; 00175 rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate); 00176 00177 rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8, 00178 1 << tbf->qt_rate.rs_cell_log, 00179 tbf->qt_rate.rs_rate); 00180 00181 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 00182 opts.mtu = tbf->qt_peakrate_txtime; 00183 tbf->qt_peakrate.rs_mpu = tbf->qt_mpu; 00184 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate); 00185 00186 rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff, 00187 tbf->qt_mpu >> 8, 00188 1 << tbf->qt_peakrate.rs_cell_log, 00189 tbf->qt_peakrate.rs_rate); 00190 } 00191 00192 msg = nlmsg_alloc(); 00193 if (!msg) 00194 goto nla_put_failure; 00195 00196 NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts); 00197 NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); 00198 00199 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) 00200 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab); 00201 00202 return msg; 00203 00204 nla_put_failure: 00205 nlmsg_free(msg); 00206 return NULL; 00207 } 00208 00209 /** 00210 * @name Attribute Access 00211 * @{ 00212 */ 00213 00214 /** 00215 * Set limit of TBF qdisc. 00216 * @arg qdisc TBF qdisc to be modified. 00217 * @arg limit New limit in bytes. 00218 * @return 0 on success or a negative error code. 00219 */ 00220 int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit) 00221 { 00222 struct rtnl_tbf *tbf; 00223 00224 tbf = tbf_alloc(qdisc); 00225 if (!tbf) 00226 return -NLE_NOMEM; 00227 00228 tbf->qt_limit = limit; 00229 tbf->qt_mask |= TBF_ATTR_LIMIT; 00230 00231 return 0; 00232 } 00233 00234 static inline double calc_limit(struct rtnl_ratespec *spec, int latency, 00235 int bucket) 00236 { 00237 double limit; 00238 00239 limit = (double) spec->rs_rate * ((double) latency / 1000000.); 00240 limit += bucket; 00241 00242 return limit; 00243 } 00244 00245 /** 00246 * Set limit of TBF qdisc by latency. 00247 * @arg qdisc TBF qdisc to be modified. 00248 * @arg latency Latency in micro seconds. 00249 * 00250 * Calculates and sets the limit based on the desired latency and the 00251 * configured rate and peak rate. In order for this operation to succeed, 00252 * the rate and if required the peak rate must have been set in advance. 00253 * 00254 * @f[ 00255 * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n 00256 * @f] 00257 * @f[ 00258 * limit = min(limit_{rate},limit_{peak}) 00259 * @f] 00260 * 00261 * @return 0 on success or a negative error code. 00262 */ 00263 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency) 00264 { 00265 struct rtnl_tbf *tbf; 00266 double limit, limit2; 00267 00268 tbf = tbf_alloc(qdisc); 00269 if (!tbf) 00270 return -NLE_NOMEM; 00271 00272 if (!(tbf->qt_mask & TBF_ATTR_RATE)) 00273 return -NLE_MISSING_ATTR; 00274 00275 limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket); 00276 00277 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 00278 limit2 = calc_limit(&tbf->qt_peakrate, latency, 00279 tbf->qt_peakrate_bucket); 00280 00281 if (limit2 < limit) 00282 limit = limit2; 00283 } 00284 00285 return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit); 00286 } 00287 00288 /** 00289 * Get limit of TBF qdisc. 00290 * @arg qdisc TBF qdisc. 00291 * @return Limit in bytes or a negative error code. 00292 */ 00293 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc) 00294 { 00295 struct rtnl_tbf *tbf; 00296 00297 tbf = tbf_qdisc(qdisc); 00298 if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT)) 00299 return tbf->qt_limit; 00300 else 00301 return -NLE_NOATTR; 00302 } 00303 00304 /** 00305 * Set MPU of TBF qdisc. 00306 * @arg qdisc TBF qdisc to be modified. 00307 * @arg mpu New MPU in bytes. 00308 * @return 0 on success or a negative error code. 00309 */ 00310 int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu) 00311 { 00312 struct rtnl_tbf *tbf; 00313 00314 tbf = tbf_alloc(qdisc); 00315 if (!tbf) 00316 return -NLE_NOMEM; 00317 00318 tbf->qt_mpu = mpu; 00319 tbf->qt_mask |= TBF_ATTR_MPU; 00320 00321 return 0; 00322 } 00323 00324 /** 00325 * Get MPU of TBF qdisc. 00326 * @arg qdisc TBF qdisc. 00327 * @return MPU in bytes or a negative error code. 00328 */ 00329 int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc) 00330 { 00331 struct rtnl_tbf *tbf; 00332 00333 tbf = tbf_qdisc(qdisc); 00334 if (tbf && (tbf->qt_mask & TBF_ATTR_MPU)) 00335 return tbf->qt_mpu; 00336 else 00337 return -NLE_NOATTR; 00338 } 00339 00340 static inline int calc_cell_log(int cell, int bucket) 00341 { 00342 if (cell > 0) 00343 cell = rtnl_tc_calc_cell_log(cell); 00344 else { 00345 cell = 0; 00346 00347 if (!bucket) 00348 bucket = 2047; /* defaults to cell_log=3 */ 00349 00350 while ((bucket >> cell) > 255) 00351 cell++; 00352 } 00353 00354 return cell; 00355 } 00356 00357 /** 00358 * Set rate of TBF qdisc. 00359 * @arg qdisc TBF qdisc to be modified. 00360 * @arg rate New rate in bytes per second. 00361 * @arg bucket Size of bucket in bytes. 00362 * @arg cell Size of a rate cell or 0 to get default value. 00363 * @return 0 on success or a negative error code. 00364 */ 00365 int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, 00366 int cell) 00367 { 00368 struct rtnl_tbf *tbf; 00369 int cell_log; 00370 00371 tbf = tbf_alloc(qdisc); 00372 if (!tbf) 00373 return -NLE_NOMEM; 00374 00375 cell_log = calc_cell_log(cell, bucket); 00376 if (cell_log < 0) 00377 return cell_log; 00378 00379 tbf->qt_rate.rs_rate = rate; 00380 tbf->qt_rate_bucket = bucket; 00381 tbf->qt_rate.rs_cell_log = cell_log; 00382 tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate); 00383 tbf->qt_mask |= TBF_ATTR_RATE; 00384 00385 return 0; 00386 } 00387 00388 /** 00389 * Get rate of TBF qdisc. 00390 * @arg qdisc TBF qdisc. 00391 * @return Rate in bytes per seconds or a negative error code. 00392 */ 00393 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc) 00394 { 00395 struct rtnl_tbf *tbf; 00396 00397 tbf = tbf_qdisc(qdisc); 00398 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 00399 return tbf->qt_rate.rs_rate; 00400 else 00401 return -1; 00402 } 00403 00404 /** 00405 * Get rate bucket size of TBF qdisc. 00406 * @arg qdisc TBF qdisc. 00407 * @return Size of rate bucket or a negative error code. 00408 */ 00409 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc) 00410 { 00411 struct rtnl_tbf *tbf; 00412 00413 tbf = tbf_qdisc(qdisc); 00414 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 00415 return tbf->qt_rate_bucket; 00416 else 00417 return -1; 00418 } 00419 00420 /** 00421 * Get rate cell size of TBF qdisc. 00422 * @arg qdisc TBF qdisc. 00423 * @return Size of rate cell in bytes or a negative error code. 00424 */ 00425 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc) 00426 { 00427 struct rtnl_tbf *tbf; 00428 00429 tbf = tbf_qdisc(qdisc); 00430 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 00431 return (1 << tbf->qt_rate.rs_cell_log); 00432 else 00433 return -1; 00434 } 00435 00436 /** 00437 * Set peak rate of TBF qdisc. 00438 * @arg qdisc TBF qdisc to be modified. 00439 * @arg rate New peak rate in bytes per second. 00440 * @arg bucket Size of peakrate bucket. 00441 * @arg cell Size of a peakrate cell or 0 to get default value. 00442 * @return 0 on success or a negative error code. 00443 */ 00444 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, 00445 int cell) 00446 { 00447 struct rtnl_tbf *tbf; 00448 int cell_log; 00449 00450 tbf = tbf_alloc(qdisc); 00451 if (!tbf) 00452 return -NLE_NOMEM; 00453 00454 cell_log = calc_cell_log(cell, bucket); 00455 if (cell_log < 0) 00456 return cell_log; 00457 00458 tbf->qt_peakrate.rs_rate = rate; 00459 tbf->qt_peakrate_bucket = bucket; 00460 tbf->qt_peakrate.rs_cell_log = cell_log; 00461 tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate); 00462 00463 tbf->qt_mask |= TBF_ATTR_PEAKRATE; 00464 00465 return 0; 00466 } 00467 00468 /** 00469 * Get peak rate of TBF qdisc. 00470 * @arg qdisc TBF qdisc. 00471 * @return Peak rate in bytes per seconds or a negative error code. 00472 */ 00473 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc) 00474 { 00475 struct rtnl_tbf *tbf; 00476 00477 tbf = tbf_qdisc(qdisc); 00478 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 00479 return tbf->qt_peakrate.rs_rate; 00480 else 00481 return -1; 00482 } 00483 00484 /** 00485 * Get peak rate bucket size of TBF qdisc. 00486 * @arg qdisc TBF qdisc. 00487 * @return Size of peak rate bucket or a negative error code. 00488 */ 00489 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc) 00490 { 00491 struct rtnl_tbf *tbf; 00492 00493 tbf = tbf_qdisc(qdisc); 00494 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 00495 return tbf->qt_peakrate_bucket; 00496 else 00497 return -1; 00498 } 00499 00500 /** 00501 * Get peak rate cell size of TBF qdisc. 00502 * @arg qdisc TBF qdisc. 00503 * @return Size of peak rate cell in bytes or a negative error code. 00504 */ 00505 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc) 00506 { 00507 struct rtnl_tbf *tbf; 00508 00509 tbf = tbf_qdisc(qdisc); 00510 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 00511 return (1 << tbf->qt_peakrate.rs_cell_log); 00512 else 00513 return -1; 00514 } 00515 00516 /** @} */ 00517 00518 static struct rtnl_qdisc_ops tbf_qdisc_ops = { 00519 .qo_kind = "tbf", 00520 .qo_msg_parser = tbf_msg_parser, 00521 .qo_dump = { 00522 [NL_DUMP_LINE] = tbf_dump_line, 00523 [NL_DUMP_DETAILS] = tbf_dump_details, 00524 }, 00525 .qo_free_data = tbf_free_data, 00526 .qo_get_opts = tbf_get_opts, 00527 }; 00528 00529 static void __init tbf_init(void) 00530 { 00531 rtnl_qdisc_register(&tbf_qdisc_ops); 00532 } 00533 00534 static void __exit tbf_exit(void) 00535 { 00536 rtnl_qdisc_unregister(&tbf_qdisc_ops); 00537 } 00538 00539 /** @} */