Home | History | Annotate | Download | only in sch
      1 /*
      2  * lib/route/sch/tbf.c		TBF Qdisc
      3  *
      4  *	This library is free software; you can redistribute it and/or
      5  *	modify it under the terms of the GNU Lesser General Public
      6  *	License as published by the Free Software Foundation version 2.1
      7  *	of the License.
      8  *
      9  * Copyright (c) 2003-2008 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup qdisc_api
     14  * @defgroup tbf Token Bucket Filter (TBF)
     15  * @{
     16  */
     17 
     18 #include <netlink-local.h>
     19 #include <netlink-tc.h>
     20 #include <netlink/netlink.h>
     21 #include <netlink/cache.h>
     22 #include <netlink/utils.h>
     23 #include <netlink/route/tc.h>
     24 #include <netlink/route/qdisc.h>
     25 #include <netlink/route/qdisc-modules.h>
     26 #include <netlink/route/class.h>
     27 #include <netlink/route/class-modules.h>
     28 #include <netlink/route/link.h>
     29 #include <netlink/route/sch/tbf.h>
     30 
     31 /** @cond SKIP */
     32 #define TBF_ATTR_LIMIT			0x01
     33 #define TBF_ATTR_RATE			0x02
     34 #define TBF_ATTR_PEAKRATE		0x10
     35 #define TBF_ATTR_MPU			0x80
     36 /** @endcond */
     37 
     38 static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc)
     39 {
     40 	return (struct rtnl_tbf *) qdisc->q_subdata;
     41 }
     42 
     43 static inline struct rtnl_tbf *tbf_alloc(struct rtnl_qdisc *qdisc)
     44 {
     45 	if (!qdisc->q_subdata)
     46 		qdisc->q_subdata = calloc(1, sizeof(struct rtnl_tbf));
     47 
     48 	return tbf_qdisc(qdisc);
     49 }
     50 
     51 static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = {
     52 	[TCA_TBF_PARMS]	= { .minlen = sizeof(struct tc_tbf_qopt) },
     53 };
     54 
     55 static int tbf_msg_parser(struct rtnl_qdisc *q)
     56 {
     57 	int err;
     58 	struct nlattr *tb[TCA_TBF_MAX + 1];
     59 	struct rtnl_tbf *tbf;
     60 
     61 	err = tca_parse(tb, TCA_TBF_MAX, (struct rtnl_tca *) q, tbf_policy);
     62 	if (err < 0)
     63 		return err;
     64 
     65 	tbf = tbf_alloc(q);
     66 	if (!tbf)
     67 		return -NLE_NOMEM;
     68 
     69 	if (tb[TCA_TBF_PARMS]) {
     70 		struct tc_tbf_qopt opts;
     71 		int bufsize;
     72 
     73 		nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts));
     74 		tbf->qt_limit = opts.limit;
     75 		tbf->qt_mpu = opts.rate.mpu;
     76 
     77 		rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate);
     78 		tbf->qt_rate_txtime = opts.buffer;
     79 		bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
     80 					       opts.rate.rate);
     81 		tbf->qt_rate_bucket = bufsize;
     82 
     83 		rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate);
     84 		tbf->qt_peakrate_txtime = opts.mtu;
     85 		bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu),
     86 					       opts.peakrate.rate);
     87 		tbf->qt_peakrate_bucket = bufsize;
     88 
     89 		tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_MPU | TBF_ATTR_RATE |
     90 				TBF_ATTR_PEAKRATE);
     91 	}
     92 
     93 	return 0;
     94 }
     95 
     96 static void tbf_free_data(struct rtnl_qdisc *qdisc)
     97 {
     98 	free(qdisc->q_subdata);
     99 }
    100 
    101 static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
    102 {
    103 	double r, rbit, lim;
    104 	char *ru, *rubit, *limu;
    105 	struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
    106 
    107 	if (!tbf)
    108 		return;
    109 
    110 	r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
    111 	rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
    112 	lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
    113 
    114 	nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
    115 		r, ru, rbit, rubit, lim, limu);
    116 }
    117 
    118 static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
    119 {
    120 	struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
    121 
    122 	if (!tbf)
    123 		return;
    124 
    125 	if (1) {
    126 		char *bu, *cu;
    127 		double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu);
    128 		double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
    129 						 &cu);
    130 
    131 		nl_dump(p, "mpu %u rate-bucket-size %1.f%s "
    132 			   "rate-cell-size %.1f%s\n",
    133 			tbf->qt_mpu, bs, bu, cl, cu);
    134 
    135 	}
    136 
    137 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
    138 		char *pru, *prbu, *bsu, *clu;
    139 		double pr, prb, bs, cl;
    140 
    141 		pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru);
    142 		prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu);
    143 		bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
    144 		cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
    145 					 &clu);
    146 
    147 		nl_dump_line(p, "    peak-rate %.2f%s/s (%.0f%s) "
    148 				"bucket-size %.1f%s cell-size %.1f%s"
    149 				"latency %.1f%s",
    150 			     pr, pru, prb, prbu, bs, bsu, cl, clu);
    151 	}
    152 }
    153 
    154 static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
    155 {
    156 	struct tc_tbf_qopt opts;
    157 	struct rtnl_tbf *tbf;
    158 	struct nl_msg *msg;
    159 	uint32_t rtab[RTNL_TC_RTABLE_SIZE];
    160 	uint32_t ptab[RTNL_TC_RTABLE_SIZE];
    161 	int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
    162 
    163 	memset(&opts, 0, sizeof(opts));
    164 
    165 	tbf = tbf_qdisc(qdisc);
    166 	if (!tbf)
    167 		return NULL;
    168 
    169 	if (!(tbf->qt_mask & required) != required)
    170 		return NULL;
    171 
    172 	opts.limit = tbf->qt_limit;
    173 	opts.buffer = tbf->qt_rate_txtime;
    174 	tbf->qt_rate.rs_mpu = tbf->qt_mpu;
    175 	rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
    176 
    177 	rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8,
    178 				 1 << tbf->qt_rate.rs_cell_log,
    179 				 tbf->qt_rate.rs_rate);
    180 
    181 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
    182 		opts.mtu = tbf->qt_peakrate_txtime;
    183 		tbf->qt_peakrate.rs_mpu = tbf->qt_mpu;
    184 		rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
    185 
    186 		rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff,
    187 					 tbf->qt_mpu >> 8,
    188 					 1 << tbf->qt_peakrate.rs_cell_log,
    189 					 tbf->qt_peakrate.rs_rate);
    190 	}
    191 
    192 	msg = nlmsg_alloc();
    193 	if (!msg)
    194 		goto nla_put_failure;
    195 
    196 	NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
    197 	NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
    198 
    199 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
    200 		NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
    201 
    202 	return msg;
    203 
    204 nla_put_failure:
    205 	nlmsg_free(msg);
    206 	return NULL;
    207 }
    208 
    209 /**
    210  * @name Attribute Access
    211  * @{
    212  */
    213 
    214 /**
    215  * Set limit of TBF qdisc.
    216  * @arg qdisc		TBF qdisc to be modified.
    217  * @arg limit		New limit in bytes.
    218  * @return 0 on success or a negative error code.
    219  */
    220 int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
    221 {
    222 	struct rtnl_tbf *tbf;
    223 
    224 	tbf = tbf_alloc(qdisc);
    225 	if (!tbf)
    226 		return -NLE_NOMEM;
    227 
    228 	tbf->qt_limit = limit;
    229 	tbf->qt_mask |= TBF_ATTR_LIMIT;
    230 
    231 	return 0;
    232 }
    233 
    234 static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
    235 				int bucket)
    236 {
    237 	double limit;
    238 
    239 	limit = (double) spec->rs_rate * ((double) latency / 1000000.);
    240 	limit += bucket;
    241 
    242 	return limit;
    243 }
    244 
    245 /**
    246  * Set limit of TBF qdisc by latency.
    247  * @arg qdisc		TBF qdisc to be modified.
    248  * @arg latency		Latency in micro seconds.
    249  *
    250  * Calculates and sets the limit based on the desired latency and the
    251  * configured rate and peak rate. In order for this operation to succeed,
    252  * the rate and if required the peak rate must have been set in advance.
    253  *
    254  * @f[
    255  *   limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n
    256  * @f]
    257  * @f[
    258  *   limit = min(limit_{rate},limit_{peak})
    259  * @f]
    260  *
    261  * @return 0 on success or a negative error code.
    262  */
    263 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
    264 {
    265 	struct rtnl_tbf *tbf;
    266 	double limit, limit2;
    267 
    268 	tbf = tbf_alloc(qdisc);
    269 	if (!tbf)
    270 		return -NLE_NOMEM;
    271 
    272 	if (!(tbf->qt_mask & TBF_ATTR_RATE))
    273 		return -NLE_MISSING_ATTR;
    274 
    275 	limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
    276 
    277 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
    278 		limit2 = calc_limit(&tbf->qt_peakrate, latency,
    279 				    tbf->qt_peakrate_bucket);
    280 
    281 		if (limit2 < limit)
    282 			limit = limit2;
    283 	}
    284 
    285 	return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
    286 }
    287 
    288 /**
    289  * Get limit of TBF qdisc.
    290  * @arg qdisc		TBF qdisc.
    291  * @return Limit in bytes or a negative error code.
    292  */
    293 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
    294 {
    295 	struct rtnl_tbf *tbf;
    296 
    297 	tbf = tbf_qdisc(qdisc);
    298 	if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT))
    299 		return tbf->qt_limit;
    300 	else
    301 		return -NLE_NOATTR;
    302 }
    303 
    304 /**
    305  * Set MPU of TBF qdisc.
    306  * @arg qdisc		TBF qdisc to be modified.
    307  * @arg mpu		New MPU in bytes.
    308  * @return 0 on success or a negative error code.
    309  */
    310 int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu)
    311 {
    312 	struct rtnl_tbf *tbf;
    313 
    314 	tbf = tbf_alloc(qdisc);
    315 	if (!tbf)
    316 		return -NLE_NOMEM;
    317 
    318 	tbf->qt_mpu = mpu;
    319 	tbf->qt_mask |= TBF_ATTR_MPU;
    320 
    321 	return 0;
    322 }
    323 
    324 /**
    325  * Get MPU of TBF qdisc.
    326  * @arg qdisc		TBF qdisc.
    327  * @return MPU in bytes or a negative error code.
    328  */
    329 int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc)
    330 {
    331 	struct rtnl_tbf *tbf;
    332 
    333 	tbf = tbf_qdisc(qdisc);
    334 	if (tbf && (tbf->qt_mask & TBF_ATTR_MPU))
    335 		return tbf->qt_mpu;
    336 	else
    337 		return -NLE_NOATTR;
    338 }
    339 
    340 static inline int calc_cell_log(int cell, int bucket)
    341 {
    342 	if (cell > 0)
    343 		cell = rtnl_tc_calc_cell_log(cell);
    344 	else {
    345 		cell = 0;
    346 
    347 		if (!bucket)
    348 			bucket = 2047; /* defaults to cell_log=3 */
    349 
    350 		while ((bucket >> cell) > 255)
    351 			cell++;
    352 	}
    353 
    354 	return cell;
    355 }
    356 
    357 /**
    358  * Set rate of TBF qdisc.
    359  * @arg qdisc		TBF qdisc to be modified.
    360  * @arg rate		New rate in bytes per second.
    361  * @arg bucket		Size of bucket in bytes.
    362  * @arg cell		Size of a rate cell or 0 to get default value.
    363  * @return 0 on success or a negative error code.
    364  */
    365 int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
    366 			    int cell)
    367 {
    368 	struct rtnl_tbf *tbf;
    369 	int cell_log;
    370 
    371 	tbf = tbf_alloc(qdisc);
    372 	if (!tbf)
    373 		return -NLE_NOMEM;
    374 
    375 	cell_log = calc_cell_log(cell, bucket);
    376 	if (cell_log < 0)
    377 		return cell_log;
    378 
    379 	tbf->qt_rate.rs_rate = rate;
    380 	tbf->qt_rate_bucket = bucket;
    381 	tbf->qt_rate.rs_cell_log = cell_log;
    382 	tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate);
    383 	tbf->qt_mask |= TBF_ATTR_RATE;
    384 
    385 	return 0;
    386 }
    387 
    388 /**
    389  * Get rate of TBF qdisc.
    390  * @arg qdisc		TBF qdisc.
    391  * @return Rate in bytes per seconds or a negative error code.
    392  */
    393 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
    394 {
    395 	struct rtnl_tbf *tbf;
    396 
    397 	tbf = tbf_qdisc(qdisc);
    398 	if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
    399 		return tbf->qt_rate.rs_rate;
    400 	else
    401 		return -1;
    402 }
    403 
    404 /**
    405  * Get rate bucket size of TBF qdisc.
    406  * @arg qdisc		TBF qdisc.
    407  * @return Size of rate bucket or a negative error code.
    408  */
    409 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
    410 {
    411 	struct rtnl_tbf *tbf;
    412 
    413 	tbf = tbf_qdisc(qdisc);
    414 	if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
    415 		return tbf->qt_rate_bucket;
    416 	else
    417 		return -1;
    418 }
    419 
    420 /**
    421  * Get rate cell size of TBF qdisc.
    422  * @arg qdisc		TBF qdisc.
    423  * @return Size of rate cell in bytes or a negative error code.
    424  */
    425 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
    426 {
    427 	struct rtnl_tbf *tbf;
    428 
    429 	tbf = tbf_qdisc(qdisc);
    430 	if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
    431 		return (1 << tbf->qt_rate.rs_cell_log);
    432 	else
    433 		return -1;
    434 }
    435 
    436 /**
    437  * Set peak rate of TBF qdisc.
    438  * @arg qdisc		TBF qdisc to be modified.
    439  * @arg rate		New peak rate in bytes per second.
    440  * @arg bucket		Size of peakrate bucket.
    441  * @arg cell		Size of a peakrate cell or 0 to get default value.
    442  * @return 0 on success or a negative error code.
    443  */
    444 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
    445 				int cell)
    446 {
    447 	struct rtnl_tbf *tbf;
    448 	int cell_log;
    449 
    450 	tbf = tbf_alloc(qdisc);
    451 	if (!tbf)
    452 		return -NLE_NOMEM;
    453 
    454 	cell_log = calc_cell_log(cell, bucket);
    455 	if (cell_log < 0)
    456 		return cell_log;
    457 
    458 	tbf->qt_peakrate.rs_rate = rate;
    459 	tbf->qt_peakrate_bucket = bucket;
    460 	tbf->qt_peakrate.rs_cell_log = cell_log;
    461 	tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate);
    462 
    463 	tbf->qt_mask |= TBF_ATTR_PEAKRATE;
    464 
    465 	return 0;
    466 }
    467 
    468 /**
    469  * Get peak rate of TBF qdisc.
    470  * @arg qdisc		TBF qdisc.
    471  * @return Peak rate in bytes per seconds or a negative error code.
    472  */
    473 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
    474 {
    475 	struct rtnl_tbf *tbf;
    476 
    477 	tbf = tbf_qdisc(qdisc);
    478 	if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
    479 		return tbf->qt_peakrate.rs_rate;
    480 	else
    481 		return -1;
    482 }
    483 
    484 /**
    485  * Get peak rate bucket size of TBF qdisc.
    486  * @arg qdisc		TBF qdisc.
    487  * @return Size of peak rate bucket or a negative error code.
    488  */
    489 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
    490 {
    491 	struct rtnl_tbf *tbf;
    492 
    493 	tbf = tbf_qdisc(qdisc);
    494 	if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
    495 		return tbf->qt_peakrate_bucket;
    496 	else
    497 		return -1;
    498 }
    499 
    500 /**
    501  * Get peak rate cell size of TBF qdisc.
    502  * @arg qdisc		TBF qdisc.
    503  * @return Size of peak rate cell in bytes or a negative error code.
    504  */
    505 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
    506 {
    507 	struct rtnl_tbf *tbf;
    508 
    509 	tbf = tbf_qdisc(qdisc);
    510 	if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
    511 		return (1 << tbf->qt_peakrate.rs_cell_log);
    512 	else
    513 		return -1;
    514 }
    515 
    516 /** @} */
    517 
    518 static struct rtnl_qdisc_ops tbf_qdisc_ops = {
    519 	.qo_kind		= "tbf",
    520 	.qo_msg_parser		= tbf_msg_parser,
    521 	.qo_dump = {
    522 	    [NL_DUMP_LINE]	= tbf_dump_line,
    523 	    [NL_DUMP_DETAILS]	= tbf_dump_details,
    524 	},
    525 	.qo_free_data		= tbf_free_data,
    526 	.qo_get_opts		= tbf_get_opts,
    527 };
    528 
    529 static void __init tbf_init(void)
    530 {
    531 	rtnl_qdisc_register(&tbf_qdisc_ops);
    532 }
    533 
    534 static void __exit tbf_exit(void)
    535 {
    536 	rtnl_qdisc_unregister(&tbf_qdisc_ops);
    537 }
    538 
    539 /** @} */
    540