Home | History | Annotate | Download | only in qdisc
      1 /*
      2  * lib/route/qdisc/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-2011 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup qdisc
     14  * @defgroup qdisc_tbf Token Bucket Filter (TBF)
     15  * @{
     16  */
     17 
     18 #include <netlink-private/netlink.h>
     19 #include <netlink-private/tc.h>
     20 #include <netlink/netlink.h>
     21 #include <netlink/cache.h>
     22 #include <netlink/utils.h>
     23 #include <netlink-private/route/tc-api.h>
     24 #include <netlink/route/qdisc.h>
     25 #include <netlink/route/class.h>
     26 #include <netlink/route/link.h>
     27 #include <netlink/route/qdisc/tbf.h>
     28 
     29 /** @cond SKIP */
     30 #define TBF_ATTR_LIMIT			0x01
     31 #define TBF_ATTR_RATE			0x02
     32 #define TBF_ATTR_PEAKRATE		0x10
     33 /** @endcond */
     34 
     35 static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = {
     36 	[TCA_TBF_PARMS]	= { .minlen = sizeof(struct tc_tbf_qopt) },
     37 };
     38 
     39 static int tbf_msg_parser(struct rtnl_tc *tc, void *data)
     40 {
     41 	struct nlattr *tb[TCA_TBF_MAX + 1];
     42 	struct rtnl_tbf *tbf = data;
     43 	int err;
     44 
     45 	if ((err = tca_parse(tb, TCA_TBF_MAX, tc, tbf_policy)) < 0)
     46 		return err;
     47 
     48 	if (tb[TCA_TBF_PARMS]) {
     49 		struct tc_tbf_qopt opts;
     50 		int bufsize;
     51 
     52 		nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts));
     53 		tbf->qt_limit = opts.limit;
     54 
     55 		rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate);
     56 		tbf->qt_rate_txtime = opts.buffer;
     57 		bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
     58 					       opts.rate.rate);
     59 		tbf->qt_rate_bucket = bufsize;
     60 
     61 		rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate);
     62 		tbf->qt_peakrate_txtime = opts.mtu;
     63 		bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu),
     64 					       opts.peakrate.rate);
     65 		tbf->qt_peakrate_bucket = bufsize;
     66 
     67 		rtnl_tc_set_mpu(tc, tbf->qt_rate.rs_mpu);
     68 		rtnl_tc_set_overhead(tc, tbf->qt_rate.rs_overhead);
     69 
     70 		tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_RATE | TBF_ATTR_PEAKRATE);
     71 	}
     72 
     73 	return 0;
     74 }
     75 
     76 static void tbf_dump_line(struct rtnl_tc *tc, void *data,
     77 			  struct nl_dump_params *p)
     78 {
     79 	double r, rbit, lim;
     80 	char *ru, *rubit, *limu;
     81 	struct rtnl_tbf *tbf = data;
     82 
     83 	if (!tbf)
     84 		return;
     85 
     86 	r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
     87 	rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
     88 	lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
     89 
     90 	nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
     91 		r, ru, rbit, rubit, lim, limu);
     92 }
     93 
     94 static void tbf_dump_details(struct rtnl_tc *tc, void *data,
     95 			     struct nl_dump_params *p)
     96 {
     97 	struct rtnl_tbf *tbf = data;
     98 
     99 	if (!tbf)
    100 		return;
    101 
    102 	if (1) {
    103 		char *bu, *cu;
    104 		double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu);
    105 		double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
    106 						 &cu);
    107 
    108 		nl_dump(p, "rate-bucket-size %1.f%s "
    109 			   "rate-cell-size %.1f%s\n",
    110 			bs, bu, cl, cu);
    111 
    112 	}
    113 
    114 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
    115 		char *pru, *prbu, *bsu, *clu;
    116 		double pr, prb, bs, cl;
    117 
    118 		pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru);
    119 		prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu);
    120 		bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
    121 		cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
    122 					 &clu);
    123 
    124 		nl_dump_line(p, "    peak-rate %.2f%s/s (%.0f%s) "
    125 				"bucket-size %.1f%s cell-size %.1f%s"
    126 				"latency %.1f%s",
    127 			     pr, pru, prb, prbu, bs, bsu, cl, clu);
    128 	}
    129 }
    130 
    131 static int tbf_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
    132 {
    133 	uint32_t rtab[RTNL_TC_RTABLE_SIZE], ptab[RTNL_TC_RTABLE_SIZE];
    134 	struct tc_tbf_qopt opts;
    135 	struct rtnl_tbf *tbf = data;
    136 	int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
    137 
    138 	if ((tbf->qt_mask & required) != required)
    139 		return -NLE_MISSING_ATTR;
    140 
    141 	memset(&opts, 0, sizeof(opts));
    142 	opts.limit = tbf->qt_limit;
    143 	opts.buffer = tbf->qt_rate_txtime;
    144 
    145 	rtnl_tc_build_rate_table(tc, &tbf->qt_rate, rtab);
    146 	rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
    147 
    148 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
    149 		opts.mtu = tbf->qt_peakrate_txtime;
    150 		rtnl_tc_build_rate_table(tc, &tbf->qt_peakrate, ptab);
    151 		rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
    152 
    153 	}
    154 
    155 	NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
    156 	NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
    157 
    158 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
    159 		NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
    160 
    161 	return 0;
    162 
    163 nla_put_failure:
    164 	return -NLE_MSGSIZE;
    165 }
    166 
    167 /**
    168  * @name Attribute Access
    169  * @{
    170  */
    171 
    172 /**
    173  * Set limit of TBF qdisc.
    174  * @arg qdisc		TBF qdisc to be modified.
    175  * @arg limit		New limit in bytes.
    176  * @return 0 on success or a negative error code.
    177  */
    178 void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
    179 {
    180 	struct rtnl_tbf *tbf;
    181 
    182 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    183 		BUG();
    184 
    185 	tbf->qt_limit = limit;
    186 	tbf->qt_mask |= TBF_ATTR_LIMIT;
    187 }
    188 
    189 static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
    190 				int bucket)
    191 {
    192 	double limit;
    193 
    194 	limit = (double) spec->rs_rate * ((double) latency / 1000000.);
    195 	limit += bucket;
    196 
    197 	return limit;
    198 }
    199 
    200 /**
    201  * Set limit of TBF qdisc by latency.
    202  * @arg qdisc		TBF qdisc to be modified.
    203  * @arg latency		Latency in micro seconds.
    204  *
    205  * Calculates and sets the limit based on the desired latency and the
    206  * configured rate and peak rate. In order for this operation to succeed,
    207  * the rate and if required the peak rate must have been set in advance.
    208  *
    209  * @f[
    210  *   limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n
    211  * @f]
    212  * @f[
    213  *   limit = min(limit_{rate},limit_{peak})
    214  * @f]
    215  *
    216  * @return 0 on success or a negative error code.
    217  */
    218 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
    219 {
    220 	struct rtnl_tbf *tbf;
    221 	double limit, limit2;
    222 
    223 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    224 		BUG();
    225 
    226 	if (!(tbf->qt_mask & TBF_ATTR_RATE))
    227 		return -NLE_MISSING_ATTR;
    228 
    229 	limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
    230 
    231 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
    232 		limit2 = calc_limit(&tbf->qt_peakrate, latency,
    233 				    tbf->qt_peakrate_bucket);
    234 
    235 		if (limit2 < limit)
    236 			limit = limit2;
    237 	}
    238 
    239 	rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
    240 
    241 	return 0;
    242 }
    243 
    244 /**
    245  * Get limit of TBF qdisc.
    246  * @arg qdisc		TBF qdisc.
    247  * @return Limit in bytes or a negative error code.
    248  */
    249 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
    250 {
    251 	struct rtnl_tbf *tbf;
    252 
    253 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    254 		BUG();
    255 
    256 	if (tbf->qt_mask & TBF_ATTR_LIMIT)
    257 		return tbf->qt_limit;
    258 	else
    259 		return -NLE_NOATTR;
    260 }
    261 
    262 static inline int calc_cell_log(int cell, int bucket)
    263 {
    264 		cell = rtnl_tc_calc_cell_log(cell);
    265 	return cell;
    266 }
    267 
    268 /**
    269  * Set rate of TBF qdisc.
    270  * @arg qdisc		TBF qdisc to be modified.
    271  * @arg rate		New rate in bytes per second.
    272  * @arg bucket		Size of bucket in bytes.
    273  * @arg cell		Size of a rate cell or 0 to get default value.
    274  * @return 0 on success or a negative error code.
    275  */
    276 void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
    277 			    int cell)
    278 {
    279 	struct rtnl_tbf *tbf;
    280 	int cell_log;
    281 
    282 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    283 		BUG();
    284 
    285 	if (!cell)
    286 		cell_log = UINT8_MAX;
    287 	else
    288 		cell_log = rtnl_tc_calc_cell_log(cell);
    289 
    290 	tbf->qt_rate.rs_rate = rate;
    291 	tbf->qt_rate_bucket = bucket;
    292 	tbf->qt_rate.rs_cell_log = cell_log;
    293 	tbf->qt_rate_txtime = nl_us2ticks(rtnl_tc_calc_txtime(bucket, rate));
    294 	tbf->qt_mask |= TBF_ATTR_RATE;
    295 }
    296 
    297 /**
    298  * Get rate of TBF qdisc.
    299  * @arg qdisc		TBF qdisc.
    300  * @return Rate in bytes per seconds or a negative error code.
    301  */
    302 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
    303 {
    304 	struct rtnl_tbf *tbf;
    305 
    306 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    307 		BUG();
    308 
    309 	if (tbf->qt_mask & TBF_ATTR_RATE)
    310 		return tbf->qt_rate.rs_rate;
    311 	else
    312 		return -1;
    313 }
    314 
    315 /**
    316  * Get rate bucket size of TBF qdisc.
    317  * @arg qdisc		TBF qdisc.
    318  * @return Size of rate bucket or a negative error code.
    319  */
    320 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
    321 {
    322 	struct rtnl_tbf *tbf;
    323 
    324 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    325 		BUG();
    326 
    327 	if (tbf->qt_mask & TBF_ATTR_RATE)
    328 		return tbf->qt_rate_bucket;
    329 	else
    330 		return -1;
    331 }
    332 
    333 /**
    334  * Get rate cell size of TBF qdisc.
    335  * @arg qdisc		TBF qdisc.
    336  * @return Size of rate cell in bytes or a negative error code.
    337  */
    338 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
    339 {
    340 	struct rtnl_tbf *tbf;
    341 
    342 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    343 		BUG();
    344 
    345 	if (tbf->qt_mask & TBF_ATTR_RATE)
    346 		return (1 << tbf->qt_rate.rs_cell_log);
    347 	else
    348 		return -1;
    349 }
    350 
    351 /**
    352  * Set peak rate of TBF qdisc.
    353  * @arg qdisc		TBF qdisc to be modified.
    354  * @arg rate		New peak rate in bytes per second.
    355  * @arg bucket		Size of peakrate bucket.
    356  * @arg cell		Size of a peakrate cell or 0 to get default value.
    357  * @return 0 on success or a negative error code.
    358  */
    359 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
    360 				int cell)
    361 {
    362 	struct rtnl_tbf *tbf;
    363 	int cell_log;
    364 
    365 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    366 		BUG();
    367 
    368 	cell_log = calc_cell_log(cell, bucket);
    369 	if (cell_log < 0)
    370 		return cell_log;
    371 
    372 	tbf->qt_peakrate.rs_rate = rate;
    373 	tbf->qt_peakrate_bucket = bucket;
    374 	tbf->qt_peakrate.rs_cell_log = cell_log;
    375 	tbf->qt_peakrate_txtime = nl_us2ticks(rtnl_tc_calc_txtime(bucket, rate));
    376 
    377 	tbf->qt_mask |= TBF_ATTR_PEAKRATE;
    378 
    379 	return 0;
    380 }
    381 
    382 /**
    383  * Get peak rate of TBF qdisc.
    384  * @arg qdisc		TBF qdisc.
    385  * @return Peak rate in bytes per seconds or a negative error code.
    386  */
    387 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
    388 {
    389 	struct rtnl_tbf *tbf;
    390 
    391 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    392 		BUG();
    393 
    394 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
    395 		return tbf->qt_peakrate.rs_rate;
    396 	else
    397 		return -1;
    398 }
    399 
    400 /**
    401  * Get peak rate bucket size of TBF qdisc.
    402  * @arg qdisc		TBF qdisc.
    403  * @return Size of peak rate bucket or a negative error code.
    404  */
    405 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
    406 {
    407 	struct rtnl_tbf *tbf;
    408 
    409 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    410 		BUG();
    411 
    412 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
    413 		return tbf->qt_peakrate_bucket;
    414 	else
    415 		return -1;
    416 }
    417 
    418 /**
    419  * Get peak rate cell size of TBF qdisc.
    420  * @arg qdisc		TBF qdisc.
    421  * @return Size of peak rate cell in bytes or a negative error code.
    422  */
    423 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
    424 {
    425 	struct rtnl_tbf *tbf;
    426 
    427 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
    428 		BUG();
    429 
    430 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
    431 		return (1 << tbf->qt_peakrate.rs_cell_log);
    432 	else
    433 		return -1;
    434 }
    435 
    436 /** @} */
    437 
    438 static struct rtnl_tc_ops tbf_tc_ops = {
    439 	.to_kind		= "tbf",
    440 	.to_type		= RTNL_TC_TYPE_QDISC,
    441 	.to_size		= sizeof(struct rtnl_tbf),
    442 	.to_msg_parser		= tbf_msg_parser,
    443 	.to_dump = {
    444 	    [NL_DUMP_LINE]	= tbf_dump_line,
    445 	    [NL_DUMP_DETAILS]	= tbf_dump_details,
    446 	},
    447 	.to_msg_fill		= tbf_msg_fill,
    448 };
    449 
    450 static void __init tbf_init(void)
    451 {
    452 	rtnl_tc_register(&tbf_tc_ops);
    453 }
    454 
    455 static void __exit tbf_exit(void)
    456 {
    457 	rtnl_tc_unregister(&tbf_tc_ops);
    458 }
    459 
    460 /** @} */
    461