tbf.c

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 /** @} */