oops
[libnl.git] / lib / route / sch / 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
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_build_no_hdr();
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 /** @} */