• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
tbf_qdisc(struct rtnl_qdisc * qdisc)38 static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc)
39 {
40 	return (struct rtnl_tbf *) qdisc->q_subdata;
41 }
42 
tbf_alloc(struct rtnl_qdisc * qdisc)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 
tbf_msg_parser(struct rtnl_qdisc * q)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 
tbf_free_data(struct rtnl_qdisc * qdisc)96 static void tbf_free_data(struct rtnl_qdisc *qdisc)
97 {
98 	free(qdisc->q_subdata);
99 }
100 
tbf_dump_line(struct rtnl_qdisc * qdisc,struct nl_dump_params * p)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 
tbf_dump_details(struct rtnl_qdisc * qdisc,struct nl_dump_params * p)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 
tbf_get_opts(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc * qdisc,int limit)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 
calc_limit(struct rtnl_ratespec * spec,int latency,int bucket)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  */
rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc * qdisc,int latency)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  */
rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc * qdisc,int mpu)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  */
rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc * qdisc)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 
calc_cell_log(int cell,int bucket)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  */
rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc * qdisc,int rate,int bucket,int cell)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  */
rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc * qdisc,int rate,int bucket,int cell)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  */
rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc * qdisc)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 
tbf_init(void)529 static void __init tbf_init(void)
530 {
531 	rtnl_qdisc_register(&tbf_qdisc_ops);
532 }
533 
tbf_exit(void)534 static void __exit tbf_exit(void)
535 {
536 	rtnl_qdisc_unregister(&tbf_qdisc_ops);
537 }
538 
539 /** @} */
540