blob: 6de87b3bbb8fb7e3f81055422ab780ffdf0eb307 [file] [log] [blame]
Thomas Graf44d36242007-09-15 01:28:01 +02001/*
2 * lib/route/sch/htb.c HTB 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 * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
11 * Copyright (c) 2005-2006 Siemens AG Oesterreich
12 */
13
14/**
15 * @ingroup qdisc_api
16 * @ingroup class_api
17 * @defgroup htb Hierachical Token Bucket (HTB)
18 * @{
19 */
20
21#include <netlink-local.h>
22#include <netlink-tc.h>
23#include <netlink/netlink.h>
24#include <netlink/cache.h>
25#include <netlink/utils.h>
26#include <netlink/route/tc.h>
27#include <netlink/route/qdisc.h>
28#include <netlink/route/qdisc-modules.h>
29#include <netlink/route/class.h>
30#include <netlink/route/class-modules.h>
31#include <netlink/route/link.h>
32#include <netlink/route/sch/htb.h>
33
34/** @cond SKIP */
35#define SCH_HTB_HAS_RATE2QUANTUM 0x01
36#define SCH_HTB_HAS_DEFCLS 0x02
37
38#define SCH_HTB_HAS_PRIO 0x001
39#define SCH_HTB_HAS_MTU 0x002
40#define SCH_HTB_HAS_RATE 0x004
41#define SCH_HTB_HAS_CEIL 0x008
42#define SCH_HTB_HAS_RBUFFER 0x010
43#define SCH_HTB_HAS_CBUFFER 0x020
44#define SCH_HTB_HAS_QUANTUM 0x040
45#define SCH_HTB_HAS_OVERHEAD 0x080
46#define SCH_HTB_HAS_MPU 0x100
47/** @endcond */
48
49static inline struct rtnl_htb_qdisc *htb_qdisc(struct rtnl_qdisc *qdisc)
50{
51 if (qdisc->q_subdata == NULL)
52 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_htb_qdisc));
53
54 return (struct rtnl_htb_qdisc *) qdisc->q_subdata;
55}
56
57static struct nla_policy htb_policy[TCA_HTB_MAX+1] = {
58 [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) },
59 [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) },
60};
61
62static int htb_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
63{
64 int err;
65 struct nlattr *tb[TCA_HTB_MAX + 1];
66 struct rtnl_htb_qdisc *d;
67
68 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) qdisc, htb_policy);
69 if (err < 0)
70 return err;
71
72 d = htb_qdisc(qdisc);
73
74 if (tb[TCA_HTB_INIT]) {
75 struct tc_htb_glob opts;
76
77 nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts));
78 d->qh_rate2quantum = opts.rate2quantum;
79 d->qh_defcls = opts.defcls;
80
81 d->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS);
82 }
83
84 return 0;
85}
86
87static void htb_qdisc_free_data(struct rtnl_qdisc *qdisc)
88{
89 free(qdisc->q_subdata);
90}
91
92static inline struct rtnl_htb_class *htb_class(struct rtnl_class *class)
93{
94 if (class->c_subdata == NULL)
95 class->c_subdata = calloc(1, sizeof(struct rtnl_htb_class));
96
97 return (struct rtnl_htb_class *) class->c_subdata;
98}
99
100static int htb_class_msg_parser(struct rtnl_class *class)
101{
102 int err;
103 struct nlattr *tb[TCA_HTB_MAX + 1];
104 struct rtnl_htb_class *d;
105
106 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) class, htb_policy);
107 if (err < 0)
108 return err;
109
110 d = htb_class(class);
111
112 if (tb[TCA_HTB_PARMS]) {
113 struct tc_htb_opt opts;
114
115 nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts));
116 d->ch_prio = opts.prio;
117 rtnl_copy_ratespec(&d->ch_rate, &opts.rate);
118 rtnl_copy_ratespec(&d->ch_ceil, &opts.ceil);
119 d->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, opts.rate.rate);
120 d->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, opts.ceil.rate);
121 d->ch_quantum = opts.quantum;
122 d->ch_overhead = (opts.rate.mpu >> 8) & 0xff;
123 d->ch_mpu = opts.rate.mpu & 0xff;
124
125 d->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE |
126 SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER |
127 SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM |
128 SCH_HTB_HAS_OVERHEAD | SCH_HTB_HAS_MPU);
129 }
130
131 return 0;
132}
133
134static void htb_class_free_data(struct rtnl_class *class)
135{
136 free(class->c_subdata);
137}
138
139static int htb_qdisc_dump_brief(struct rtnl_qdisc *qdisc,
140 struct nl_dump_params *p, int line)
141{
142 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
143
144 if (d == NULL)
145 goto ignore;
146
147 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
148 dp_dump(p, " r2q %u", d->qh_rate2quantum);
149
150 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) {
151 char buf[32];
152 dp_dump(p, " default %s",
153 rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf)));
154 }
155
156ignore:
157 return line;
158}
159
160static int htb_class_dump_brief(struct rtnl_class *class,
161 struct nl_dump_params *p, int line)
162{
163 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
164
165 if (d == NULL)
166 goto ignore;
167
168 if (d->ch_mask & SCH_HTB_HAS_RATE) {
169 double r, rbit;
170 char *ru, *rubit;
171
172 r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru);
173 rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit);
174
175 dp_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
176 r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log);
177 }
178
179ignore:
180 return line;
181}
182
183static int htb_class_dump_full(struct rtnl_class *class,
184 struct nl_dump_params *p, int line)
185{
186 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
187
188 if (d == NULL)
189 goto ignore;
190
191 /* line 1 */
192 if (d->ch_mask & SCH_HTB_HAS_CEIL) {
193 double r, rbit;
194 char *ru, *rubit;
195
196 r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru);
197 rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit);
198
199 dp_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
200 r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log);
201 }
202
203 if (d->ch_mask & SCH_HTB_HAS_PRIO)
204 dp_dump(p, " prio %u", d->ch_prio);
205
206 if (d->ch_mask & SCH_HTB_HAS_MTU)
207 dp_dump(p, " mtu %u", d->ch_mtu);
208
209 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) {
210 double b;
211 char *bu;
212
213 b = nl_cancel_down_bytes(d->ch_rbuffer, &bu);
214 dp_dump(p, " rbuffer %.2f%s", b, bu);
215 }
216
217 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) {
218 double b;
219 char *bu;
220
221 b = nl_cancel_down_bytes(d->ch_cbuffer, &bu);
222 dp_dump(p, " cbuffer %.2f%s", b, bu);
223 }
224
225 if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
226 dp_dump(p, " quantum %u", d->ch_quantum);
227
228 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
229 dp_dump(p, " overhead %u", d->ch_overhead);
230
231 if (d->ch_mask & SCH_HTB_HAS_MPU)
232 dp_dump(p, " mpu %u", d->ch_mpu);
233
234ignore:
235 return line;
236}
237
238static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc)
239{
240 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
241 struct tc_htb_glob opts;
242 struct nl_msg *msg;
243
244 if (d == NULL)
245 return NULL;
246
247 msg = nlmsg_alloc();
248 if (msg == NULL)
249 return NULL;
250
251 memset(&opts, 0, sizeof(opts));
252 opts.version = TC_HTB_PROTOVER;
253
254 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
255 opts.rate2quantum = d->qh_rate2quantum;
256 if (d->qh_mask & SCH_HTB_HAS_DEFCLS)
257 opts.defcls = d->qh_defcls;
258
259 nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
260
261 return msg;
262}
263
264static uint8_t compute_cell(uint32_t rate, uint32_t mtu)
265{
266 uint8_t cell_log = 0;
267 while (mtu > 255) {
268 mtu >>= 1;
269 cell_log++;
270 }
271
272 return cell_log;
273}
274
275static struct nl_msg *htb_class_get_opts(struct rtnl_class *class)
276{
277 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
278 uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
279 struct tc_htb_opt opts;
280 struct nl_msg *msg;
281 int buffer, cbuffer;
282 uint8_t overhead = 0, mpu = 0;
283
284 if (d == NULL)
285 return NULL;
286
287 msg = nlmsg_alloc();
288 memset(&opts, 0, sizeof(opts));
289
290 /* if not set, zero (0) is used as priority */
291 if (d->ch_mask & SCH_HTB_HAS_PRIO)
292 opts.prio = d->ch_prio;
293
294 if (d->ch_mask & SCH_HTB_HAS_MTU)
295 mtu = d->ch_mtu;
296 else
297 mtu = 1600; /* eth packet len */
298
299 if (!(d->ch_mask & SCH_HTB_HAS_RATE))
300 BUG();
301
302 rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate);
303 /* if cell_log not set, compute default value */
304 if (opts.rate.cell_log == UINT8_MAX)
305 opts.rate.cell_log = compute_cell(opts.rate.rate, mtu);
306
307 /* if not set, configured rate is used as ceil, which implies no borrowing */
308 if (d->ch_mask & SCH_HTB_HAS_CEIL)
309 rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil);
310 else
311 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
312 /* if cell_log not set, compute default value */
313 if (opts.ceil.cell_log == UINT8_MAX)
314 opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu);
315
316 if (d->ch_mask & SCH_HTB_HAS_RBUFFER)
317 buffer = d->ch_rbuffer;
318 else
319 buffer = opts.rate.rate / nl_get_hz() + mtu;
320
321 opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate);
322
323 if (d->ch_mask & SCH_HTB_HAS_CBUFFER)
324 cbuffer = d->ch_cbuffer;
325 else
326 cbuffer = opts.ceil.rate / nl_get_hz() + mtu;
327
328 opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate);
329
330 if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
331 opts.quantum = d->ch_quantum;
332
333 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
334 overhead = d->ch_overhead;
335
336 if (d->ch_mask & SCH_HTB_HAS_MPU)
337 mpu = d->ch_mpu;
338
339 opts.rate.mpu = mpu | (overhead << 8);
340 opts.ceil.mpu = mpu | (overhead << 8);
341
342 nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
343
344 rtnl_tc_build_rate_table(rtable, mpu, overhead,
345 1 << opts.rate.cell_log,
346 opts.rate.rate);
347 nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
348
349 rtnl_tc_build_rate_table(ctable, mpu, overhead,
350 1 << opts.ceil.cell_log,
351 opts.ceil.rate);
352 nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
353
354 return msg;
355}
356
357/**
358 * @name Attribute Modifications
359 * @{
360 */
361
362void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
363{
364 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
365 if (d == NULL)
366 return;
367
368 d->qh_rate2quantum = rate2quantum;
369 d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
370}
371
372/**
373 * Set default class of the htb qdisc to the specified value
374 * @arg qdisc qdisc to change
375 * @arg defcls new default class
376 */
377void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
378{
379 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
380 if (d == NULL)
381 return;
382
383 d->qh_defcls = defcls;
384 d->qh_mask |= SCH_HTB_HAS_DEFCLS;
385}
386
387void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
388{
389 struct rtnl_htb_class *d = htb_class(class);
390 if (d == NULL)
391 return;
392
393 d->ch_prio = prio;
394 d->ch_mask |= SCH_HTB_HAS_PRIO;
395}
396
397/**
398 * Set MTU of the data link.
399 * @arg class HTB class to be modified.
400 * @arg mtu New MTU in bytes.
401 *
402 * Sets MTU of the data link controlled by the HTB class.
403 * If not set, the Ethernet MTU (1600) is used.
404 */
405void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu)
406{
407 struct rtnl_htb_class *d = htb_class(class);
408 if (d == NULL)
409 return;
410
411 d->ch_mtu = mtu;
412 d->ch_mask |= SCH_HTB_HAS_MTU;
413}
414
415/**
416 * Set rate of HTB class.
417 * @arg class HTB class to be modified.
418 * @arg rate New rate in bytes per second.
419 */
420void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
421{
422 struct rtnl_htb_class *d = htb_class(class);
423 if (d == NULL)
424 return;
425
426 d->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */
427 d->ch_rate.rs_rate = rate;
428 d->ch_mask |= SCH_HTB_HAS_RATE;
429}
430
431/**
432 * Set ceil of HTB class.
433 * @arg class HTB class to be modified.
434 * @arg ceil New ceil in bytes per second.
435 */
436void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
437{
438 struct rtnl_htb_class *d = htb_class(class);
439 if (d == NULL)
440 return;
441
442 d->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */
443 d->ch_ceil.rs_rate = ceil;
444 d->ch_mask |= SCH_HTB_HAS_CEIL;
445}
446
447/**
448 * Set size of the rate bucket of HTB class.
449 * @arg class HTB class to be modified.
450 * @arg rbuffer New size in bytes.
451 */
452void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
453{
454 struct rtnl_htb_class *d = htb_class(class);
455 if (d == NULL)
456 return;
457
458 d->ch_rbuffer = rbuffer;
459 d->ch_mask |= SCH_HTB_HAS_RBUFFER;
460}
461
462/**
463 * Set size of the ceil bucket of HTB class.
464 * @arg class HTB class to be modified.
465 * @arg cbuffer New size in bytes.
466 */
467void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
468{
469 struct rtnl_htb_class *d = htb_class(class);
470 if (d == NULL)
471 return;
472
473 d->ch_cbuffer = cbuffer;
474 d->ch_mask |= SCH_HTB_HAS_CBUFFER;
475}
476
477/**
478 * Set how much bytes to serve from leaf at once of HTB class {use r2q}.
479 * @arg class HTB class to be modified.
480 * @arg quantum New size in bytes.
481 */
482void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
483{
484 struct rtnl_htb_class *d = htb_class(class);
485 if (d == NULL)
486 return;
487
488 d->ch_quantum = quantum;
489 d->ch_mask |= SCH_HTB_HAS_QUANTUM;
490}
491
492/**
493 * Set per-packet size overhead used in rate computations of HTB class.
494 * @arg class HTB class to be modified.
495 * @arg overhead Size in bytes.
496 */
497void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead)
498{
499 struct rtnl_htb_class *d = htb_class(class);
500 if (d == NULL)
501 return;
502
503 d->ch_overhead = overhead;
504 d->ch_mask |= SCH_HTB_HAS_OVERHEAD;
505}
506
507/**
508 * Set the minimum packet size used in rate computations of HTB class.
509 * @arg class HTB class to be modified.
510 * @arg mpu Size in bytes.
511 */
512void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu)
513{
514 struct rtnl_htb_class *d = htb_class(class);
515 if (d == NULL)
516 return;
517
518 d->ch_mpu = mpu;
519 d->ch_mask |= SCH_HTB_HAS_MPU;
520}
521
522/** @} */
523
524static struct rtnl_qdisc_ops htb_qdisc_ops = {
525 .qo_kind = "htb",
526 .qo_msg_parser = htb_qdisc_msg_parser,
527 .qo_free_data = htb_qdisc_free_data,
528 .qo_dump[NL_DUMP_BRIEF] = htb_qdisc_dump_brief,
529 .qo_get_opts = htb_qdisc_get_opts,
530};
531
532static struct rtnl_class_ops htb_class_ops = {
533 .co_kind = "htb",
534 .co_msg_parser = htb_class_msg_parser,
535 .co_free_data = htb_class_free_data,
536 .co_dump[NL_DUMP_BRIEF] = htb_class_dump_brief,
537 .co_dump[NL_DUMP_FULL] = htb_class_dump_full,
538 .co_get_opts = htb_class_get_opts,
539};
540
541static void __init htb_init(void)
542{
543 rtnl_qdisc_register(&htb_qdisc_ops);
544 rtnl_class_register(&htb_class_ops);
545}
546
547static void __exit htb_exit(void)
548{
549 rtnl_qdisc_unregister(&htb_qdisc_ops);
550 rtnl_class_unregister(&htb_class_ops);
551}
552
553/** @} */