• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
tbf_msg_parser(struct rtnl_tc * tc,void * data)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_bufsize64(nl_ticks2us(opts.buffer),
58 		                                 tbf->qt_rate.rs_rate64);
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_bufsize64(nl_ticks2us(opts.mtu),
64 		                                 tbf->qt_peakrate.rs_rate64);
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 
tbf_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)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_rate64, &ru);
87 	rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate64*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 
tbf_dump_details(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)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_rate64, &pru);
119 		prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate64 * 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 
tbf_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)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  */
rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc * qdisc,int limit)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 
calc_limit(struct rtnl_ratespec * spec,int latency,int bucket)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_rate64 * ((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  */
rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc * qdisc,int latency)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  */
rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc * qdisc)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 
calc_cell_log(int cell,int bucket)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  */
rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc * qdisc,int rate,int bucket,int cell)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_rate64 = (uint32_t)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_txtime64(bucket, tbf->qt_rate.rs_rate64));
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  */
rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc * qdisc)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_rate64;
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  */
rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc * qdisc,int rate,int bucket,int cell)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_rate64 = (uint32_t)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_txtime64(bucket, tbf->qt_peakrate.rs_rate64));
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  */
rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc * qdisc)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_rate64;
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  */
rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc * qdisc)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 
tbf_init(void)450 static void __init tbf_init(void)
451 {
452 	rtnl_tc_register(&tbf_tc_ops);
453 }
454 
tbf_exit(void)455 static void __exit tbf_exit(void)
456 {
457 	rtnl_tc_unregister(&tbf_tc_ops);
458 }
459 
460 /** @} */
461