123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- /*
- * lib/route/sch/tbf.c TBF Qdisc
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
- /**
- * @ingroup qdisc_api
- * @defgroup tbf Token Bucket Filter (TBF)
- * @{
- */
- #include <netlink-local.h>
- #include <netlink-tc.h>
- #include <netlink/netlink.h>
- #include <netlink/cache.h>
- #include <netlink/utils.h>
- #include <netlink/route/tc.h>
- #include <netlink/route/qdisc.h>
- #include <netlink/route/qdisc-modules.h>
- #include <netlink/route/class.h>
- #include <netlink/route/class-modules.h>
- #include <netlink/route/link.h>
- #include <netlink/route/sch/tbf.h>
- /** @cond SKIP */
- #define TBF_ATTR_LIMIT 0x01
- #define TBF_ATTR_RATE 0x02
- #define TBF_ATTR_PEAKRATE 0x10
- #define TBF_ATTR_MPU 0x80
- /** @endcond */
- static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc)
- {
- return (struct rtnl_tbf *) qdisc->q_subdata;
- }
- static inline struct rtnl_tbf *tbf_alloc(struct rtnl_qdisc *qdisc)
- {
- if (!qdisc->q_subdata)
- qdisc->q_subdata = calloc(1, sizeof(struct rtnl_tbf));
- return tbf_qdisc(qdisc);
- }
- static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = {
- [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) },
- };
- static int tbf_msg_parser(struct rtnl_qdisc *q)
- {
- int err;
- struct nlattr *tb[TCA_TBF_MAX + 1];
- struct rtnl_tbf *tbf;
- err = tca_parse(tb, TCA_TBF_MAX, (struct rtnl_tca *) q, tbf_policy);
- if (err < 0)
- return err;
-
- tbf = tbf_qdisc(q);
- if (!tbf)
- return nl_errno(ENOMEM);
- if (tb[TCA_TBF_PARMS]) {
- struct tc_tbf_qopt opts;
- int bufsize;
- nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts));
- tbf->qt_limit = opts.limit;
- tbf->qt_mpu = opts.rate.mpu;
-
- rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate);
- tbf->qt_rate_txtime = opts.buffer;
- bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
- opts.rate.rate);
- tbf->qt_rate_bucket = bufsize;
- rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate);
- tbf->qt_peakrate_txtime = opts.mtu;
- bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu),
- opts.peakrate.rate);
- tbf->qt_peakrate_bucket = bufsize;
- tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_MPU | TBF_ATTR_RATE |
- TBF_ATTR_PEAKRATE);
- }
- return 0;
- }
- static int tbf_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
- int line)
- {
- double r, rbit, lim;
- char *ru, *rubit, *limu;
- struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
- if (!tbf)
- goto ignore;
- r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
- rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
- lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
- dp_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
- r, ru, rbit, rubit, lim, limu);
- ignore:
- return line;
- }
- static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
- int line)
- {
- struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
- if (!tbf)
- goto ignore;
- if (1) {
- char *bu, *cu;
- double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu);
- double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
- &cu);
- dp_dump(p, "mpu %u rate-bucket-size %1.f%s "
- "rate-cell-size %.1f%s\n",
- tbf->qt_mpu, bs, bu, cl, cu);
- }
- if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
- char *pru, *prbu, *bsu, *clu;
- double pr, prb, bs, cl;
-
- pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru);
- prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu);
- bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
- cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
- &clu);
- dp_dump_line(p, line++, " peak-rate %.2f%s/s (%.0f%s) "
- "bucket-size %.1f%s cell-size %.1f%s",
- "latency %.1f%s",
- pr, pru, prb, prbu, bs, bsu, cl, clu);
- }
- ignore:
- return line;
- }
- static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
- {
- struct tc_tbf_qopt opts;
- struct rtnl_tbf *tbf;
- struct nl_msg *msg;
- uint32_t rtab[RTNL_TC_RTABLE_SIZE];
- uint32_t ptab[RTNL_TC_RTABLE_SIZE];
- int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
- memset(&opts, 0, sizeof(opts));
- tbf = tbf_qdisc(qdisc);
- if (!tbf)
- return NULL;
- if (!(tbf->qt_mask & required) != required)
- return NULL;
- opts.limit = tbf->qt_limit;
- opts.buffer = tbf->qt_rate_txtime;
- tbf->qt_rate.rs_mpu = tbf->qt_mpu;
- rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
- rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8,
- 1 << tbf->qt_rate.rs_cell_log,
- tbf->qt_rate.rs_rate);
- if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
- opts.mtu = tbf->qt_peakrate_txtime;
- tbf->qt_peakrate.rs_mpu = tbf->qt_mpu;
- rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
- rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff,
- tbf->qt_mpu >> 8,
- 1 << tbf->qt_peakrate.rs_cell_log,
- tbf->qt_peakrate.rs_rate);
- }
- msg = nlmsg_alloc();
- if (!msg)
- goto nla_put_failure;
- NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
- NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
- if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
- NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
- return msg;
- nla_put_failure:
- nlmsg_free(msg);
- return NULL;
- }
- /**
- * @name Attribute Access
- * @{
- */
- /**
- * Set limit of TBF qdisc.
- * @arg qdisc TBF qdisc to be modified.
- * @arg limit New limit in bytes.
- * @return 0 on success or a negative error code.
- */
- int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
- {
- struct rtnl_tbf *tbf;
-
- tbf = tbf_alloc(qdisc);
- if (!tbf)
- return nl_errno(ENOMEM);
- tbf->qt_limit = limit;
- tbf->qt_mask |= TBF_ATTR_LIMIT;
- return 0;
- }
- static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
- int bucket)
- {
- double limit;
- limit = (double) spec->rs_rate * ((double) latency / 1000000.);
- limit += bucket;
- return limit;
- }
- /**
- * Set limit of TBF qdisc by latency.
- * @arg qdisc TBF qdisc to be modified.
- * @arg latency Latency in micro seconds.
- *
- * Calculates and sets the limit based on the desired latency and the
- * configured rate and peak rate. In order for this operation to succeed,
- * the rate and if required the peak rate must have been set in advance.
- *
- * @f[
- * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n
- * @f]
- * @f[
- * limit = min(limit_{rate},limit_{peak})
- * @f]
- *
- * @return 0 on success or a negative error code.
- */
- int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
- {
- struct rtnl_tbf *tbf;
- double limit, limit2;
- tbf = tbf_alloc(qdisc);
- if (!tbf)
- return nl_errno(ENOMEM);
- if (!(tbf->qt_mask & TBF_ATTR_RATE))
- return nl_error(EINVAL, "The rate must be specified before "
- "limit can be calculated based on latency.");
- limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
- if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
- limit2 = calc_limit(&tbf->qt_peakrate, latency,
- tbf->qt_peakrate_bucket);
- if (limit2 < limit)
- limit = limit2;
- }
- return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
- }
- /**
- * Get limit of TBF qdisc.
- * @arg qdisc TBF qdisc.
- * @return Limit in bytes or a negative error code.
- */
- int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
- {
- struct rtnl_tbf *tbf;
-
- tbf = tbf_qdisc(qdisc);
- if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT))
- return tbf->qt_limit;
- return
- nl_errno(ENOENT);
- }
- /**
- * Set MPU of TBF qdisc.
- * @arg qdisc TBF qdisc to be modified.
- * @arg mpu New MPU in bytes.
- * @return 0 on success or a negative error code.
- */
- int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu)
- {
- struct rtnl_tbf *tbf;
-
- tbf = tbf_alloc(qdisc);
- if (!tbf)
- return nl_errno(ENOMEM);
- tbf->qt_mpu = mpu;
- tbf->qt_mask |= TBF_ATTR_MPU;
- return 0;
- }
- /**
- * Get MPU of TBF qdisc.
- * @arg qdisc TBF qdisc.
- * @return MPU in bytes or a negative error code.
- */
- int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc)
- {
- struct rtnl_tbf *tbf;
-
- tbf = tbf_qdisc(qdisc);
- if (tbf && (tbf->qt_mask & TBF_ATTR_MPU))
- return tbf->qt_mpu;
- return
- nl_errno(ENOENT);
- }
- static inline int calc_cell_log(int cell, int bucket)
- {
- if (cell > 0)
- cell = rtnl_tc_calc_cell_log(cell);
- else {
- cell = 0;
- if (!bucket)
- bucket = 2047; /* defaults to cell_log=3 */
- while ((bucket >> cell) > 255)
- cell++;
- }
- return cell;
- }
- /**
- * Set rate of TBF qdisc.
- * @arg qdisc TBF qdisc to be modified.
- * @arg rate New rate in bytes per second.
- * @arg bucket Size of bucket in bytes.
- * @arg cell Size of a rate cell or 0 to get default value.
- * @return 0 on success or a negative error code.
- */
- int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
- int cell)
- {
- struct rtnl_tbf *tbf;
- int cell_log;
-
- tbf = tbf_alloc(qdisc);
- if (!tbf)
- return nl_errno(ENOMEM);
- cell_log = calc_cell_log(cell, bucket);
- if (cell_log < 0)
- return cell_log;
- tbf->qt_rate.rs_rate = rate;
- tbf->qt_rate_bucket = bucket;
- tbf->qt_rate.rs_cell_log = cell_log;
- tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate);
- tbf->qt_mask |= TBF_ATTR_RATE;
- return 0;
- }
- /**
- * Get rate of TBF qdisc.
- * @arg qdisc TBF qdisc.
- * @return Rate in bytes per seconds or a negative error code.
- */
- int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
- {
- struct rtnl_tbf *tbf;
- tbf = tbf_qdisc(qdisc);
- if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
- return tbf->qt_rate.rs_rate;
- else
- return -1;
- }
- /**
- * Get rate bucket size of TBF qdisc.
- * @arg qdisc TBF qdisc.
- * @return Size of rate bucket or a negative error code.
- */
- int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
- {
- struct rtnl_tbf *tbf;
- tbf = tbf_qdisc(qdisc);
- if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
- return tbf->qt_rate_bucket;
- else
- return -1;
- }
- /**
- * Get rate cell size of TBF qdisc.
- * @arg qdisc TBF qdisc.
- * @return Size of rate cell in bytes or a negative error code.
- */
- int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
- {
- struct rtnl_tbf *tbf;
- tbf = tbf_qdisc(qdisc);
- if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
- return (1 << tbf->qt_rate.rs_cell_log);
- else
- return -1;
- }
- /**
- * Set peak rate of TBF qdisc.
- * @arg qdisc TBF qdisc to be modified.
- * @arg rate New peak rate in bytes per second.
- * @arg bucket Size of peakrate bucket.
- * @arg cell Size of a peakrate cell or 0 to get default value.
- * @return 0 on success or a negative error code.
- */
- int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
- int cell)
- {
- struct rtnl_tbf *tbf;
- int cell_log;
-
- tbf = tbf_alloc(qdisc);
- if (!tbf)
- return nl_errno(ENOMEM);
- cell_log = calc_cell_log(cell, bucket);
- if (cell_log < 0)
- return cell_log;
- tbf->qt_peakrate.rs_rate = rate;
- tbf->qt_peakrate_bucket = bucket;
- tbf->qt_peakrate.rs_cell_log = cell_log;
- tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate);
-
- tbf->qt_mask |= TBF_ATTR_PEAKRATE;
- return 0;
- }
- /**
- * Get peak rate of TBF qdisc.
- * @arg qdisc TBF qdisc.
- * @return Peak rate in bytes per seconds or a negative error code.
- */
- int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
- {
- struct rtnl_tbf *tbf;
- tbf = tbf_qdisc(qdisc);
- if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
- return tbf->qt_peakrate.rs_rate;
- else
- return -1;
- }
- /**
- * Get peak rate bucket size of TBF qdisc.
- * @arg qdisc TBF qdisc.
- * @return Size of peak rate bucket or a negative error code.
- */
- int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
- {
- struct rtnl_tbf *tbf;
- tbf = tbf_qdisc(qdisc);
- if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
- return tbf->qt_peakrate_bucket;
- else
- return -1;
- }
- /**
- * Get peak rate cell size of TBF qdisc.
- * @arg qdisc TBF qdisc.
- * @return Size of peak rate cell in bytes or a negative error code.
- */
- int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
- {
- struct rtnl_tbf *tbf;
- tbf = tbf_qdisc(qdisc);
- if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
- return (1 << tbf->qt_peakrate.rs_cell_log);
- else
- return -1;
- }
- /** @} */
- static struct rtnl_qdisc_ops tbf_qdisc_ops = {
- .qo_kind = "tbf",
- .qo_msg_parser = tbf_msg_parser,
- .qo_dump[NL_DUMP_BRIEF] = tbf_dump_brief,
- .qo_dump[NL_DUMP_FULL] = tbf_dump_full,
- .qo_get_opts = tbf_get_opts,
- };
- static void __init tbf_init(void)
- {
- rtnl_qdisc_register(&tbf_qdisc_ops);
- }
- static void __exit tbf_exit(void)
- {
- rtnl_qdisc_unregister(&tbf_qdisc_ops);
- }
- /** @} */
|