libnl 1.1
lib/route/sch/tbf.c
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 /** @} */