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