Tizen 2.1 base
[platform/upstream/libnl2.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-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
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_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
96 static void tbf_free_data(struct rtnl_qdisc *qdisc)
97 {
98         free(qdisc->q_subdata);
99 }
100
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
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
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  */
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
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  */
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  */
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  */
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  */
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
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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
529 static void __init tbf_init(void)
530 {
531         rtnl_qdisc_register(&tbf_qdisc_ops);
532 }
533
534 static void __exit tbf_exit(void)
535 {
536         rtnl_qdisc_unregister(&tbf_qdisc_ops);
537 }
538
539 /** @} */