libnl  1.1
tbf.c
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-2006 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 
38 static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc)
39 {
40  return (struct rtnl_tbf *) qdisc->q_subdata;
41 }
42 
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 
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_qdisc(q);
66  if (!tbf)
67  return nl_errno(ENOMEM);
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 
96 static int tbf_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
97  int line)
98 {
99  double r, rbit, lim;
100  char *ru, *rubit, *limu;
101  struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
102 
103  if (!tbf)
104  goto ignore;
105 
106  r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
107  rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
108  lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
109 
110  dp_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
111  r, ru, rbit, rubit, lim, limu);
112 
113 ignore:
114  return line;
115 }
116 
117 static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
118  int line)
119 {
120  struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
121 
122  if (!tbf)
123  goto ignore;
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  dp_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  dp_dump_line(p, line++, " 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 ignore:
154  return line;
155 }
156 
157 static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
158 {
159  struct tc_tbf_qopt opts;
160  struct rtnl_tbf *tbf;
161  struct nl_msg *msg;
162  uint32_t rtab[RTNL_TC_RTABLE_SIZE];
163  uint32_t ptab[RTNL_TC_RTABLE_SIZE];
164  int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
165 
166  memset(&opts, 0, sizeof(opts));
167 
168  tbf = tbf_qdisc(qdisc);
169  if (!tbf)
170  return NULL;
171 
172  if (!(tbf->qt_mask & required) != required)
173  return NULL;
174 
175  opts.limit = tbf->qt_limit;
176  opts.buffer = tbf->qt_rate_txtime;
177  tbf->qt_rate.rs_mpu = tbf->qt_mpu;
178  rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
179 
180  rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8,
181  1 << tbf->qt_rate.rs_cell_log,
182  tbf->qt_rate.rs_rate);
183 
184  if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
185  opts.mtu = tbf->qt_peakrate_txtime;
186  tbf->qt_peakrate.rs_mpu = tbf->qt_mpu;
187  rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
188 
189  rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff,
190  tbf->qt_mpu >> 8,
191  1 << tbf->qt_peakrate.rs_cell_log,
192  tbf->qt_peakrate.rs_rate);
193  }
194 
195  msg = nlmsg_alloc();
196  if (!msg)
197  goto nla_put_failure;
198 
199  NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
200  NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
201 
202  if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
203  NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
204 
205  return msg;
206 
207 nla_put_failure:
208  nlmsg_free(msg);
209  return NULL;
210 }
211 
212 /**
213  * @name Attribute Access
214  * @{
215  */
216 
217 /**
218  * Set limit of TBF qdisc.
219  * @arg qdisc TBF qdisc to be modified.
220  * @arg limit New limit in bytes.
221  * @return 0 on success or a negative error code.
222  */
223 int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
224 {
225  struct rtnl_tbf *tbf;
226 
227  tbf = tbf_alloc(qdisc);
228  if (!tbf)
229  return nl_errno(ENOMEM);
230 
231  tbf->qt_limit = limit;
232  tbf->qt_mask |= TBF_ATTR_LIMIT;
233 
234  return 0;
235 }
236 
237 static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
238  int bucket)
239 {
240  double limit;
241 
242  limit = (double) spec->rs_rate * ((double) latency / 1000000.);
243  limit += bucket;
244 
245  return limit;
246 }
247 
248 /**
249  * Set limit of TBF qdisc by latency.
250  * @arg qdisc TBF qdisc to be modified.
251  * @arg latency Latency in micro seconds.
252  *
253  * Calculates and sets the limit based on the desired latency and the
254  * configured rate and peak rate. In order for this operation to succeed,
255  * the rate and if required the peak rate must have been set in advance.
256  *
257  * @f[
258  * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n
259  * @f]
260  * @f[
261  * limit = min(limit_{rate},limit_{peak})
262  * @f]
263  *
264  * @return 0 on success or a negative error code.
265  */
266 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
267 {
268  struct rtnl_tbf *tbf;
269  double limit, limit2;
270 
271  tbf = tbf_alloc(qdisc);
272  if (!tbf)
273  return nl_errno(ENOMEM);
274 
275  if (!(tbf->qt_mask & TBF_ATTR_RATE))
276  return nl_error(EINVAL, "The rate must be specified before "
277  "limit can be calculated based on latency.");
278 
279  limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
280 
281  if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
282  limit2 = calc_limit(&tbf->qt_peakrate, latency,
283  tbf->qt_peakrate_bucket);
284 
285  if (limit2 < limit)
286  limit = limit2;
287  }
288 
289  return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
290 }
291 
292 /**
293  * Get limit of TBF qdisc.
294  * @arg qdisc TBF qdisc.
295  * @return Limit in bytes or a negative error code.
296  */
297 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
298 {
299  struct rtnl_tbf *tbf;
300 
301  tbf = tbf_qdisc(qdisc);
302  if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT))
303  return tbf->qt_limit;
304  return
305  nl_errno(ENOENT);
306 }
307 
308 /**
309  * Set MPU of TBF qdisc.
310  * @arg qdisc TBF qdisc to be modified.
311  * @arg mpu New MPU in bytes.
312  * @return 0 on success or a negative error code.
313  */
314 int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu)
315 {
316  struct rtnl_tbf *tbf;
317 
318  tbf = tbf_alloc(qdisc);
319  if (!tbf)
320  return nl_errno(ENOMEM);
321 
322  tbf->qt_mpu = mpu;
323  tbf->qt_mask |= TBF_ATTR_MPU;
324 
325  return 0;
326 }
327 
328 /**
329  * Get MPU of TBF qdisc.
330  * @arg qdisc TBF qdisc.
331  * @return MPU in bytes or a negative error code.
332  */
333 int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc)
334 {
335  struct rtnl_tbf *tbf;
336 
337  tbf = tbf_qdisc(qdisc);
338  if (tbf && (tbf->qt_mask & TBF_ATTR_MPU))
339  return tbf->qt_mpu;
340  return
341  nl_errno(ENOENT);
342 }
343 
344 static inline int calc_cell_log(int cell, int bucket)
345 {
346  if (cell > 0)
347  cell = rtnl_tc_calc_cell_log(cell);
348  else {
349  cell = 0;
350 
351  if (!bucket)
352  bucket = 2047; /* defaults to cell_log=3 */
353 
354  while ((bucket >> cell) > 255)
355  cell++;
356  }
357 
358  return cell;
359 }
360 
361 /**
362  * Set rate of TBF qdisc.
363  * @arg qdisc TBF qdisc to be modified.
364  * @arg rate New rate in bytes per second.
365  * @arg bucket Size of bucket in bytes.
366  * @arg cell Size of a rate cell or 0 to get default value.
367  * @return 0 on success or a negative error code.
368  */
369 int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
370  int cell)
371 {
372  struct rtnl_tbf *tbf;
373  int cell_log;
374 
375  tbf = tbf_alloc(qdisc);
376  if (!tbf)
377  return nl_errno(ENOMEM);
378 
379  cell_log = calc_cell_log(cell, bucket);
380  if (cell_log < 0)
381  return cell_log;
382 
383  tbf->qt_rate.rs_rate = rate;
384  tbf->qt_rate_bucket = bucket;
385  tbf->qt_rate.rs_cell_log = cell_log;
386  tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate);
387  tbf->qt_mask |= TBF_ATTR_RATE;
388 
389  return 0;
390 }
391 
392 /**
393  * Get rate of TBF qdisc.
394  * @arg qdisc TBF qdisc.
395  * @return Rate in bytes per seconds or a negative error code.
396  */
397 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
398 {
399  struct rtnl_tbf *tbf;
400 
401  tbf = tbf_qdisc(qdisc);
402  if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
403  return tbf->qt_rate.rs_rate;
404  else
405  return -1;
406 }
407 
408 /**
409  * Get rate bucket size of TBF qdisc.
410  * @arg qdisc TBF qdisc.
411  * @return Size of rate bucket or a negative error code.
412  */
413 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
414 {
415  struct rtnl_tbf *tbf;
416 
417  tbf = tbf_qdisc(qdisc);
418  if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
419  return tbf->qt_rate_bucket;
420  else
421  return -1;
422 }
423 
424 /**
425  * Get rate cell size of TBF qdisc.
426  * @arg qdisc TBF qdisc.
427  * @return Size of rate cell in bytes or a negative error code.
428  */
429 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
430 {
431  struct rtnl_tbf *tbf;
432 
433  tbf = tbf_qdisc(qdisc);
434  if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
435  return (1 << tbf->qt_rate.rs_cell_log);
436  else
437  return -1;
438 }
439 
440 /**
441  * Set peak rate of TBF qdisc.
442  * @arg qdisc TBF qdisc to be modified.
443  * @arg rate New peak rate in bytes per second.
444  * @arg bucket Size of peakrate bucket.
445  * @arg cell Size of a peakrate cell or 0 to get default value.
446  * @return 0 on success or a negative error code.
447  */
448 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
449  int cell)
450 {
451  struct rtnl_tbf *tbf;
452  int cell_log;
453 
454  tbf = tbf_alloc(qdisc);
455  if (!tbf)
456  return nl_errno(ENOMEM);
457 
458  cell_log = calc_cell_log(cell, bucket);
459  if (cell_log < 0)
460  return cell_log;
461 
462  tbf->qt_peakrate.rs_rate = rate;
463  tbf->qt_peakrate_bucket = bucket;
464  tbf->qt_peakrate.rs_cell_log = cell_log;
465  tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate);
466 
467  tbf->qt_mask |= TBF_ATTR_PEAKRATE;
468 
469  return 0;
470 }
471 
472 /**
473  * Get peak rate of TBF qdisc.
474  * @arg qdisc TBF qdisc.
475  * @return Peak rate in bytes per seconds or a negative error code.
476  */
477 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
478 {
479  struct rtnl_tbf *tbf;
480 
481  tbf = tbf_qdisc(qdisc);
482  if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
483  return tbf->qt_peakrate.rs_rate;
484  else
485  return -1;
486 }
487 
488 /**
489  * Get peak rate bucket size of TBF qdisc.
490  * @arg qdisc TBF qdisc.
491  * @return Size of peak rate bucket or a negative error code.
492  */
493 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
494 {
495  struct rtnl_tbf *tbf;
496 
497  tbf = tbf_qdisc(qdisc);
498  if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
499  return tbf->qt_peakrate_bucket;
500  else
501  return -1;
502 }
503 
504 /**
505  * Get peak rate cell size of TBF qdisc.
506  * @arg qdisc TBF qdisc.
507  * @return Size of peak rate cell in bytes or a negative error code.
508  */
509 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
510 {
511  struct rtnl_tbf *tbf;
512 
513  tbf = tbf_qdisc(qdisc);
514  if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
515  return (1 << tbf->qt_peakrate.rs_cell_log);
516  else
517  return -1;
518 }
519 
520 /** @} */
521 
522 static struct rtnl_qdisc_ops tbf_qdisc_ops = {
523  .qo_kind = "tbf",
524  .qo_msg_parser = tbf_msg_parser,
525  .qo_dump[NL_DUMP_BRIEF] = tbf_dump_brief,
526  .qo_dump[NL_DUMP_FULL] = tbf_dump_full,
527  .qo_get_opts = tbf_get_opts,
528 };
529 
530 static void __init tbf_init(void)
531 {
532  rtnl_qdisc_register(&tbf_qdisc_ops);
533 }
534 
535 static void __exit tbf_exit(void)
536 {
537  rtnl_qdisc_unregister(&tbf_qdisc_ops);
538 }
539 
540 /** @} */
Dump object in a brief one-liner.
Definition: types.h:22
int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
Get peak rate bucket size of TBF qdisc.
Definition: tbf.c:493
char qo_kind[32]
Kind/Name of Qdisc.
Definition: qdisc-modules.h:30
int rtnl_tc_calc_txtime(int bufsize, int rate)
Calculate time required to transmit buffer at a specific rate.
Definition: tc.c:383
#define RTNL_TC_RTABLE_SIZE
Number of entries in a transmission time lookup table.
Definition: tc.h:50
int rtnl_tc_calc_cell_log(int cell_size)
Calculate the binary logarithm for a specific cell size.
Definition: tc.c:420
attribute validation policy
Definition: attr.h:73
double nl_cancel_down_bits(unsigned long long l, char **unit)
Cancel down a bit counter.
Definition: utils.c:218
int rtnl_qdisc_unregister(struct rtnl_qdisc_ops *qops)
Unregister a qdisc module.
Definition: qdisc_api.c:61
struct nl_msg * nlmsg_alloc(void)
Allocate a new netlink message with the default maximum payload size.
Definition: msg.c:401
void nlmsg_free(struct nl_msg *n)
Free a netlink message.
Definition: msg.c:656
int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
Get peak rate of TBF qdisc.
Definition: tbf.c:477
int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc)
Get MPU of TBF qdisc.
Definition: tbf.c:333
int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu)
Set MPU of TBF qdisc.
Definition: tbf.c:314
int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
Get limit of TBF qdisc.
Definition: tbf.c:297
#define NLA_PUT(n, attrtype, attrlen, data)
Add a netlink attribute to a netlink message.
Definition: attr.h:157
int nla_memcpy(void *dest, struct nlattr *src, int count)
Copy a netlink attribute into another memory area.
Definition: attr.c:383
int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
Set limit of TBF qdisc by latency.
Definition: tbf.c:266
int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
Get peak rate cell size of TBF qdisc.
Definition: tbf.c:509
int rtnl_tc_calc_bufsize(int txtime, int rate)
Calculate buffer size able to transmit in a specific time and rate.
Definition: tc.c:406
double nl_cancel_down_bytes(unsigned long long l, char **unit)
Cancel down a byte counter.
Definition: utils.c:188
int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
Set limit of TBF qdisc.
Definition: tbf.c:223
int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, int cell)
Set peak rate of TBF qdisc.
Definition: tbf.c:448
int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
Get rate cell size of TBF qdisc.
Definition: tbf.c:429
uint32_t nl_ticks2us(uint32_t ticks)
Convert ticks to micro seconds.
Definition: utils.c:440
uint16_t minlen
Minimal length of payload required to be available.
Definition: attr.h:78
int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
Get rate of TBF qdisc.
Definition: tbf.c:397
int rtnl_qdisc_register(struct rtnl_qdisc_ops *qops)
Register a qdisc module.
Definition: qdisc_api.c:40
Dumping parameters.
Definition: types.h:36
int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, int cell)
Set rate of TBF qdisc.
Definition: tbf.c:369
Dump all attributes but no statistics.
Definition: types.h:23
int rtnl_tc_build_rate_table(uint32_t *dst, uint8_t mpu, uint8_t overhead, int cell, int rate)
Compute a transmission time lookup table.
Definition: tc.c:454
Qdisc Operations.
Definition: qdisc-modules.h:25
int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
Get rate bucket size of TBF qdisc.
Definition: tbf.c:413