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