1 /*
2 * lib/route/qdisc/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-2011 Thomas Graf <tgraf@suug.ch>
10 */
11
12 /**
13 * @ingroup qdisc
14 * @defgroup qdisc_tbf Token Bucket Filter (TBF)
15 * @{
16 */
17
18 #include <netlink-private/netlink.h>
19 #include <netlink-private/tc.h>
20 #include <netlink/netlink.h>
21 #include <netlink/cache.h>
22 #include <netlink/utils.h>
23 #include <netlink-private/route/tc-api.h>
24 #include <netlink/route/qdisc.h>
25 #include <netlink/route/class.h>
26 #include <netlink/route/link.h>
27 #include <netlink/route/qdisc/tbf.h>
28
29 /** @cond SKIP */
30 #define TBF_ATTR_LIMIT 0x01
31 #define TBF_ATTR_RATE 0x02
32 #define TBF_ATTR_PEAKRATE 0x10
33 /** @endcond */
34
35 static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = {
36 [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) },
37 };
38
tbf_msg_parser(struct rtnl_tc * tc,void * data)39 static int tbf_msg_parser(struct rtnl_tc *tc, void *data)
40 {
41 struct nlattr *tb[TCA_TBF_MAX + 1];
42 struct rtnl_tbf *tbf = data;
43 int err;
44
45 if ((err = tca_parse(tb, TCA_TBF_MAX, tc, tbf_policy)) < 0)
46 return err;
47
48 if (tb[TCA_TBF_PARMS]) {
49 struct tc_tbf_qopt opts;
50 int bufsize;
51
52 nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts));
53 tbf->qt_limit = opts.limit;
54
55 rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate);
56 tbf->qt_rate_txtime = opts.buffer;
57 bufsize = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.buffer),
58 tbf->qt_rate.rs_rate64);
59 tbf->qt_rate_bucket = bufsize;
60
61 rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate);
62 tbf->qt_peakrate_txtime = opts.mtu;
63 bufsize = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.mtu),
64 tbf->qt_peakrate.rs_rate64);
65 tbf->qt_peakrate_bucket = bufsize;
66
67 rtnl_tc_set_mpu(tc, tbf->qt_rate.rs_mpu);
68 rtnl_tc_set_overhead(tc, tbf->qt_rate.rs_overhead);
69
70 tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_RATE | TBF_ATTR_PEAKRATE);
71 }
72
73 return 0;
74 }
75
tbf_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)76 static void tbf_dump_line(struct rtnl_tc *tc, void *data,
77 struct nl_dump_params *p)
78 {
79 double r, rbit, lim;
80 char *ru, *rubit, *limu;
81 struct rtnl_tbf *tbf = data;
82
83 if (!tbf)
84 return;
85
86 r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate64, &ru);
87 rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate64*8, &rubit);
88 lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
89
90 nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
91 r, ru, rbit, rubit, lim, limu);
92 }
93
tbf_dump_details(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)94 static void tbf_dump_details(struct rtnl_tc *tc, void *data,
95 struct nl_dump_params *p)
96 {
97 struct rtnl_tbf *tbf = data;
98
99 if (!tbf)
100 return;
101
102 if (1) {
103 char *bu, *cu;
104 double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu);
105 double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
106 &cu);
107
108 nl_dump(p, "rate-bucket-size %1.f%s "
109 "rate-cell-size %.1f%s\n",
110 bs, bu, cl, cu);
111
112 }
113
114 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
115 char *pru, *prbu, *bsu, *clu;
116 double pr, prb, bs, cl;
117
118 pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate64, &pru);
119 prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate64 * 8, &prbu);
120 bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
121 cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
122 &clu);
123
124 nl_dump_line(p, " peak-rate %.2f%s/s (%.0f%s) "
125 "bucket-size %.1f%s cell-size %.1f%s"
126 "latency %.1f%s",
127 pr, pru, prb, prbu, bs, bsu, cl, clu);
128 }
129 }
130
tbf_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)131 static int tbf_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
132 {
133 uint32_t rtab[RTNL_TC_RTABLE_SIZE], ptab[RTNL_TC_RTABLE_SIZE];
134 struct tc_tbf_qopt opts;
135 struct rtnl_tbf *tbf = data;
136 int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
137
138 if ((tbf->qt_mask & required) != required)
139 return -NLE_MISSING_ATTR;
140
141 memset(&opts, 0, sizeof(opts));
142 opts.limit = tbf->qt_limit;
143 opts.buffer = tbf->qt_rate_txtime;
144
145 rtnl_tc_build_rate_table(tc, &tbf->qt_rate, rtab);
146 rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
147
148 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
149 opts.mtu = tbf->qt_peakrate_txtime;
150 rtnl_tc_build_rate_table(tc, &tbf->qt_peakrate, ptab);
151 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
152
153 }
154
155 NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
156 NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
157
158 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
159 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
160
161 return 0;
162
163 nla_put_failure:
164 return -NLE_MSGSIZE;
165 }
166
167 /**
168 * @name Attribute Access
169 * @{
170 */
171
172 /**
173 * Set limit of TBF qdisc.
174 * @arg qdisc TBF qdisc to be modified.
175 * @arg limit New limit in bytes.
176 * @return 0 on success or a negative error code.
177 */
rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc * qdisc,int limit)178 void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
179 {
180 struct rtnl_tbf *tbf;
181
182 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
183 BUG();
184
185 tbf->qt_limit = limit;
186 tbf->qt_mask |= TBF_ATTR_LIMIT;
187 }
188
calc_limit(struct rtnl_ratespec * spec,int latency,int bucket)189 static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
190 int bucket)
191 {
192 double limit;
193
194 limit = (double) spec->rs_rate64 * ((double) latency / 1000000.);
195 limit += bucket;
196
197 return limit;
198 }
199
200 /**
201 * Set limit of TBF qdisc by latency.
202 * @arg qdisc TBF qdisc to be modified.
203 * @arg latency Latency in micro seconds.
204 *
205 * Calculates and sets the limit based on the desired latency and the
206 * configured rate and peak rate. In order for this operation to succeed,
207 * the rate and if required the peak rate must have been set in advance.
208 *
209 * @f[
210 * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n
211 * @f]
212 * @f[
213 * limit = min(limit_{rate},limit_{peak})
214 * @f]
215 *
216 * @return 0 on success or a negative error code.
217 */
rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc * qdisc,int latency)218 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
219 {
220 struct rtnl_tbf *tbf;
221 double limit, limit2;
222
223 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
224 BUG();
225
226 if (!(tbf->qt_mask & TBF_ATTR_RATE))
227 return -NLE_MISSING_ATTR;
228
229 limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
230
231 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
232 limit2 = calc_limit(&tbf->qt_peakrate, latency,
233 tbf->qt_peakrate_bucket);
234
235 if (limit2 < limit)
236 limit = limit2;
237 }
238
239 rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
240
241 return 0;
242 }
243
244 /**
245 * Get limit of TBF qdisc.
246 * @arg qdisc TBF qdisc.
247 * @return Limit in bytes or a negative error code.
248 */
rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc * qdisc)249 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
250 {
251 struct rtnl_tbf *tbf;
252
253 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
254 BUG();
255
256 if (tbf->qt_mask & TBF_ATTR_LIMIT)
257 return tbf->qt_limit;
258 else
259 return -NLE_NOATTR;
260 }
261
calc_cell_log(int cell,int bucket)262 static inline int calc_cell_log(int cell, int bucket)
263 {
264 cell = rtnl_tc_calc_cell_log(cell);
265 return cell;
266 }
267
268 /**
269 * Set rate of TBF qdisc.
270 * @arg qdisc TBF qdisc to be modified.
271 * @arg rate New rate in bytes per second.
272 * @arg bucket Size of bucket in bytes.
273 * @arg cell Size of a rate cell or 0 to get default value.
274 * @return 0 on success or a negative error code.
275 */
rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc * qdisc,int rate,int bucket,int cell)276 void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
277 int cell)
278 {
279 struct rtnl_tbf *tbf;
280 int cell_log;
281
282 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
283 BUG();
284
285 if (!cell)
286 cell_log = UINT8_MAX;
287 else
288 cell_log = rtnl_tc_calc_cell_log(cell);
289
290 tbf->qt_rate.rs_rate64 = (uint32_t)rate;
291 tbf->qt_rate_bucket = bucket;
292 tbf->qt_rate.rs_cell_log = cell_log;
293 tbf->qt_rate_txtime = nl_us2ticks(rtnl_tc_calc_txtime64(bucket, tbf->qt_rate.rs_rate64));
294 tbf->qt_mask |= TBF_ATTR_RATE;
295 }
296
297 /**
298 * Get rate of TBF qdisc.
299 * @arg qdisc TBF qdisc.
300 * @return Rate in bytes per seconds or a negative error code.
301 */
rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc * qdisc)302 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
303 {
304 struct rtnl_tbf *tbf;
305
306 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
307 BUG();
308
309 if (tbf->qt_mask & TBF_ATTR_RATE)
310 return tbf->qt_rate.rs_rate64;
311 else
312 return -1;
313 }
314
315 /**
316 * Get rate bucket size of TBF qdisc.
317 * @arg qdisc TBF qdisc.
318 * @return Size of rate bucket or a negative error code.
319 */
rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc * qdisc)320 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
321 {
322 struct rtnl_tbf *tbf;
323
324 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
325 BUG();
326
327 if (tbf->qt_mask & TBF_ATTR_RATE)
328 return tbf->qt_rate_bucket;
329 else
330 return -1;
331 }
332
333 /**
334 * Get rate cell size of TBF qdisc.
335 * @arg qdisc TBF qdisc.
336 * @return Size of rate cell in bytes or a negative error code.
337 */
rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc * qdisc)338 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
339 {
340 struct rtnl_tbf *tbf;
341
342 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
343 BUG();
344
345 if (tbf->qt_mask & TBF_ATTR_RATE)
346 return (1 << tbf->qt_rate.rs_cell_log);
347 else
348 return -1;
349 }
350
351 /**
352 * Set peak rate of TBF qdisc.
353 * @arg qdisc TBF qdisc to be modified.
354 * @arg rate New peak rate in bytes per second.
355 * @arg bucket Size of peakrate bucket.
356 * @arg cell Size of a peakrate cell or 0 to get default value.
357 * @return 0 on success or a negative error code.
358 */
rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc * qdisc,int rate,int bucket,int cell)359 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
360 int cell)
361 {
362 struct rtnl_tbf *tbf;
363 int cell_log;
364
365 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
366 BUG();
367
368 cell_log = calc_cell_log(cell, bucket);
369 if (cell_log < 0)
370 return cell_log;
371
372 tbf->qt_peakrate.rs_rate64 = (uint32_t)rate;
373 tbf->qt_peakrate_bucket = bucket;
374 tbf->qt_peakrate.rs_cell_log = cell_log;
375 tbf->qt_peakrate_txtime = nl_us2ticks(rtnl_tc_calc_txtime64(bucket, tbf->qt_peakrate.rs_rate64));
376
377 tbf->qt_mask |= TBF_ATTR_PEAKRATE;
378
379 return 0;
380 }
381
382 /**
383 * Get peak rate of TBF qdisc.
384 * @arg qdisc TBF qdisc.
385 * @return Peak rate in bytes per seconds or a negative error code.
386 */
rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc * qdisc)387 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
388 {
389 struct rtnl_tbf *tbf;
390
391 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
392 BUG();
393
394 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
395 return tbf->qt_peakrate.rs_rate64;
396 else
397 return -1;
398 }
399
400 /**
401 * Get peak rate bucket size of TBF qdisc.
402 * @arg qdisc TBF qdisc.
403 * @return Size of peak rate bucket or a negative error code.
404 */
rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc * qdisc)405 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
406 {
407 struct rtnl_tbf *tbf;
408
409 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
410 BUG();
411
412 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
413 return tbf->qt_peakrate_bucket;
414 else
415 return -1;
416 }
417
418 /**
419 * Get peak rate cell size of TBF qdisc.
420 * @arg qdisc TBF qdisc.
421 * @return Size of peak rate cell in bytes or a negative error code.
422 */
rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc * qdisc)423 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
424 {
425 struct rtnl_tbf *tbf;
426
427 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
428 BUG();
429
430 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
431 return (1 << tbf->qt_peakrate.rs_cell_log);
432 else
433 return -1;
434 }
435
436 /** @} */
437
438 static struct rtnl_tc_ops tbf_tc_ops = {
439 .to_kind = "tbf",
440 .to_type = RTNL_TC_TYPE_QDISC,
441 .to_size = sizeof(struct rtnl_tbf),
442 .to_msg_parser = tbf_msg_parser,
443 .to_dump = {
444 [NL_DUMP_LINE] = tbf_dump_line,
445 [NL_DUMP_DETAILS] = tbf_dump_details,
446 },
447 .to_msg_fill = tbf_msg_fill,
448 };
449
tbf_init(void)450 static void __init tbf_init(void)
451 {
452 rtnl_tc_register(&tbf_tc_ops);
453 }
454
tbf_exit(void)455 static void __exit tbf_exit(void)
456 {
457 rtnl_tc_unregister(&tbf_tc_ops);
458 }
459
460 /** @} */
461