libnl
3.2.3
|
00001 /* 00002 * lib/route/qdisc/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-2011 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup qdisc 00014 * @defgroup qdisc_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-api.h> 00024 #include <netlink/route/qdisc.h> 00025 #include <netlink/route/class.h> 00026 #include <netlink/route/link.h> 00027 #include <netlink/route/qdisc/tbf.h> 00028 00029 /** @cond SKIP */ 00030 #define TBF_ATTR_LIMIT 0x01 00031 #define TBF_ATTR_RATE 0x02 00032 #define TBF_ATTR_PEAKRATE 0x10 00033 /** @endcond */ 00034 00035 static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = { 00036 [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) }, 00037 }; 00038 00039 static int tbf_msg_parser(struct rtnl_tc *tc, void *data) 00040 { 00041 struct nlattr *tb[TCA_TBF_MAX + 1]; 00042 struct rtnl_tbf *tbf = data; 00043 int err; 00044 00045 if ((err = tca_parse(tb, TCA_TBF_MAX, tc, tbf_policy)) < 0) 00046 return err; 00047 00048 if (tb[TCA_TBF_PARMS]) { 00049 struct tc_tbf_qopt opts; 00050 int bufsize; 00051 00052 nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts)); 00053 tbf->qt_limit = opts.limit; 00054 00055 rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate); 00056 tbf->qt_rate_txtime = opts.buffer; 00057 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer), 00058 opts.rate.rate); 00059 tbf->qt_rate_bucket = bufsize; 00060 00061 rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate); 00062 tbf->qt_peakrate_txtime = opts.mtu; 00063 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu), 00064 opts.peakrate.rate); 00065 tbf->qt_peakrate_bucket = bufsize; 00066 00067 rtnl_tc_set_mpu(tc, tbf->qt_rate.rs_mpu); 00068 rtnl_tc_set_overhead(tc, tbf->qt_rate.rs_overhead); 00069 00070 tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_RATE | TBF_ATTR_PEAKRATE); 00071 } 00072 00073 return 0; 00074 } 00075 00076 static void tbf_dump_line(struct rtnl_tc *tc, void *data, 00077 struct nl_dump_params *p) 00078 { 00079 double r, rbit, lim; 00080 char *ru, *rubit, *limu; 00081 struct rtnl_tbf *tbf = data; 00082 00083 if (!tbf) 00084 return; 00085 00086 r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru); 00087 rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit); 00088 lim = nl_cancel_down_bytes(tbf->qt_limit, &limu); 00089 00090 nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", 00091 r, ru, rbit, rubit, lim, limu); 00092 } 00093 00094 static void tbf_dump_details(struct rtnl_tc *tc, void *data, 00095 struct nl_dump_params *p) 00096 { 00097 struct rtnl_tbf *tbf = data; 00098 00099 if (!tbf) 00100 return; 00101 00102 if (1) { 00103 char *bu, *cu; 00104 double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu); 00105 double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log, 00106 &cu); 00107 00108 nl_dump(p, "rate-bucket-size %1.f%s " 00109 "rate-cell-size %.1f%s\n", 00110 bs, bu, cl, cu); 00111 00112 } 00113 00114 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 00115 char *pru, *prbu, *bsu, *clu; 00116 double pr, prb, bs, cl; 00117 00118 pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru); 00119 prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu); 00120 bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu); 00121 cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log, 00122 &clu); 00123 00124 nl_dump_line(p, " peak-rate %.2f%s/s (%.0f%s) " 00125 "bucket-size %.1f%s cell-size %.1f%s" 00126 "latency %.1f%s", 00127 pr, pru, prb, prbu, bs, bsu, cl, clu); 00128 } 00129 } 00130 00131 static int tbf_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) 00132 { 00133 uint32_t rtab[RTNL_TC_RTABLE_SIZE], ptab[RTNL_TC_RTABLE_SIZE]; 00134 struct tc_tbf_qopt opts; 00135 struct rtnl_tbf *tbf = data; 00136 int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT; 00137 00138 if (!(tbf->qt_mask & required) != required) 00139 return -NLE_MISSING_ATTR; 00140 00141 memset(&opts, 0, sizeof(opts)); 00142 opts.limit = tbf->qt_limit; 00143 opts.buffer = tbf->qt_rate_txtime; 00144 00145 rtnl_tc_build_rate_table(tc, &tbf->qt_rate, rtab); 00146 rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate); 00147 00148 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 00149 opts.mtu = tbf->qt_peakrate_txtime; 00150 rtnl_tc_build_rate_table(tc, &tbf->qt_peakrate, ptab); 00151 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate); 00152 00153 } 00154 00155 NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts); 00156 NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); 00157 00158 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) 00159 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab); 00160 00161 return 0; 00162 00163 nla_put_failure: 00164 return -NLE_MSGSIZE; 00165 } 00166 00167 /** 00168 * @name Attribute Access 00169 * @{ 00170 */ 00171 00172 /** 00173 * Set limit of TBF qdisc. 00174 * @arg qdisc TBF qdisc to be modified. 00175 * @arg limit New limit in bytes. 00176 * @return 0 on success or a negative error code. 00177 */ 00178 void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit) 00179 { 00180 struct rtnl_tbf *tbf; 00181 00182 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00183 BUG(); 00184 00185 tbf->qt_limit = limit; 00186 tbf->qt_mask |= TBF_ATTR_LIMIT; 00187 } 00188 00189 static inline double calc_limit(struct rtnl_ratespec *spec, int latency, 00190 int bucket) 00191 { 00192 double limit; 00193 00194 limit = (double) spec->rs_rate * ((double) latency / 1000000.); 00195 limit += bucket; 00196 00197 return limit; 00198 } 00199 00200 /** 00201 * Set limit of TBF qdisc by latency. 00202 * @arg qdisc TBF qdisc to be modified. 00203 * @arg latency Latency in micro seconds. 00204 * 00205 * Calculates and sets the limit based on the desired latency and the 00206 * configured rate and peak rate. In order for this operation to succeed, 00207 * the rate and if required the peak rate must have been set in advance. 00208 * 00209 * @f[ 00210 * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n 00211 * @f] 00212 * @f[ 00213 * limit = min(limit_{rate},limit_{peak}) 00214 * @f] 00215 * 00216 * @return 0 on success or a negative error code. 00217 */ 00218 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency) 00219 { 00220 struct rtnl_tbf *tbf; 00221 double limit, limit2; 00222 00223 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00224 BUG(); 00225 00226 if (!(tbf->qt_mask & TBF_ATTR_RATE)) 00227 return -NLE_MISSING_ATTR; 00228 00229 limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket); 00230 00231 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 00232 limit2 = calc_limit(&tbf->qt_peakrate, latency, 00233 tbf->qt_peakrate_bucket); 00234 00235 if (limit2 < limit) 00236 limit = limit2; 00237 } 00238 00239 rtnl_qdisc_tbf_set_limit(qdisc, (int) limit); 00240 00241 return 0; 00242 } 00243 00244 /** 00245 * Get limit of TBF qdisc. 00246 * @arg qdisc TBF qdisc. 00247 * @return Limit in bytes or a negative error code. 00248 */ 00249 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc) 00250 { 00251 struct rtnl_tbf *tbf; 00252 00253 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00254 BUG(); 00255 00256 if (tbf->qt_mask & TBF_ATTR_LIMIT) 00257 return tbf->qt_limit; 00258 else 00259 return -NLE_NOATTR; 00260 } 00261 00262 static inline int calc_cell_log(int cell, int bucket) 00263 { 00264 cell = rtnl_tc_calc_cell_log(cell); 00265 return cell; 00266 } 00267 00268 /** 00269 * Set rate of TBF qdisc. 00270 * @arg qdisc TBF qdisc to be modified. 00271 * @arg rate New rate in bytes per second. 00272 * @arg bucket Size of bucket in bytes. 00273 * @arg cell Size of a rate cell or 0 to get default value. 00274 * @return 0 on success or a negative error code. 00275 */ 00276 void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, 00277 int cell) 00278 { 00279 struct rtnl_tbf *tbf; 00280 int cell_log; 00281 00282 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00283 BUG(); 00284 00285 if (!cell) 00286 cell_log = UINT8_MAX; 00287 else 00288 cell_log = rtnl_tc_calc_cell_log(cell); 00289 00290 tbf->qt_rate.rs_rate = rate; 00291 tbf->qt_rate_bucket = bucket; 00292 tbf->qt_rate.rs_cell_log = cell_log; 00293 tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate); 00294 tbf->qt_mask |= TBF_ATTR_RATE; 00295 } 00296 00297 /** 00298 * Get rate of TBF qdisc. 00299 * @arg qdisc TBF qdisc. 00300 * @return Rate in bytes per seconds or a negative error code. 00301 */ 00302 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc) 00303 { 00304 struct rtnl_tbf *tbf; 00305 00306 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00307 BUG(); 00308 00309 if (tbf->qt_mask & TBF_ATTR_RATE) 00310 return tbf->qt_rate.rs_rate; 00311 else 00312 return -1; 00313 } 00314 00315 /** 00316 * Get rate bucket size of TBF qdisc. 00317 * @arg qdisc TBF qdisc. 00318 * @return Size of rate bucket or a negative error code. 00319 */ 00320 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc) 00321 { 00322 struct rtnl_tbf *tbf; 00323 00324 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00325 BUG(); 00326 00327 if (tbf->qt_mask & TBF_ATTR_RATE) 00328 return tbf->qt_rate_bucket; 00329 else 00330 return -1; 00331 } 00332 00333 /** 00334 * Get rate cell size of TBF qdisc. 00335 * @arg qdisc TBF qdisc. 00336 * @return Size of rate cell in bytes or a negative error code. 00337 */ 00338 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc) 00339 { 00340 struct rtnl_tbf *tbf; 00341 00342 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00343 BUG(); 00344 00345 if (tbf->qt_mask & TBF_ATTR_RATE) 00346 return (1 << tbf->qt_rate.rs_cell_log); 00347 else 00348 return -1; 00349 } 00350 00351 /** 00352 * Set peak rate of TBF qdisc. 00353 * @arg qdisc TBF qdisc to be modified. 00354 * @arg rate New peak rate in bytes per second. 00355 * @arg bucket Size of peakrate bucket. 00356 * @arg cell Size of a peakrate cell or 0 to get default value. 00357 * @return 0 on success or a negative error code. 00358 */ 00359 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, 00360 int cell) 00361 { 00362 struct rtnl_tbf *tbf; 00363 int cell_log; 00364 00365 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00366 BUG(); 00367 00368 cell_log = calc_cell_log(cell, bucket); 00369 if (cell_log < 0) 00370 return cell_log; 00371 00372 tbf->qt_peakrate.rs_rate = rate; 00373 tbf->qt_peakrate_bucket = bucket; 00374 tbf->qt_peakrate.rs_cell_log = cell_log; 00375 tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate); 00376 00377 tbf->qt_mask |= TBF_ATTR_PEAKRATE; 00378 00379 return 0; 00380 } 00381 00382 /** 00383 * Get peak rate of TBF qdisc. 00384 * @arg qdisc TBF qdisc. 00385 * @return Peak rate in bytes per seconds or a negative error code. 00386 */ 00387 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc) 00388 { 00389 struct rtnl_tbf *tbf; 00390 00391 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00392 BUG(); 00393 00394 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) 00395 return tbf->qt_peakrate.rs_rate; 00396 else 00397 return -1; 00398 } 00399 00400 /** 00401 * Get peak rate bucket size of TBF qdisc. 00402 * @arg qdisc TBF qdisc. 00403 * @return Size of peak rate bucket or a negative error code. 00404 */ 00405 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc) 00406 { 00407 struct rtnl_tbf *tbf; 00408 00409 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00410 BUG(); 00411 00412 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) 00413 return tbf->qt_peakrate_bucket; 00414 else 00415 return -1; 00416 } 00417 00418 /** 00419 * Get peak rate cell size of TBF qdisc. 00420 * @arg qdisc TBF qdisc. 00421 * @return Size of peak rate cell in bytes or a negative error code. 00422 */ 00423 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc) 00424 { 00425 struct rtnl_tbf *tbf; 00426 00427 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) 00428 BUG(); 00429 00430 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) 00431 return (1 << tbf->qt_peakrate.rs_cell_log); 00432 else 00433 return -1; 00434 } 00435 00436 /** @} */ 00437 00438 static struct rtnl_tc_ops tbf_tc_ops = { 00439 .to_kind = "tbf", 00440 .to_type = RTNL_TC_TYPE_QDISC, 00441 .to_size = sizeof(struct rtnl_tbf), 00442 .to_msg_parser = tbf_msg_parser, 00443 .to_dump = { 00444 [NL_DUMP_LINE] = tbf_dump_line, 00445 [NL_DUMP_DETAILS] = tbf_dump_details, 00446 }, 00447 .to_msg_fill = tbf_msg_fill, 00448 }; 00449 00450 static void __init tbf_init(void) 00451 { 00452 rtnl_tc_register(&tbf_tc_ops); 00453 } 00454 00455 static void __exit tbf_exit(void) 00456 { 00457 rtnl_tc_unregister(&tbf_tc_ops); 00458 } 00459 00460 /** @} */