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