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