Home | History | Annotate | Download | only in sch
      1 /*
      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-2008 Thomas Graf <tgraf (at) suug.ch>
     10  * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard (at) 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 
     49 static 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 
     57 static 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 
     62 static 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 
     87 static void htb_qdisc_free_data(struct rtnl_qdisc *qdisc)
     88 {
     89 	free(qdisc->q_subdata);
     90 }
     91 
     92 static 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 
    100 static 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 
    134 static void htb_class_free_data(struct rtnl_class *class)
    135 {
    136 	free(class->c_subdata);
    137 }
    138 
    139 static void htb_qdisc_dump_line(struct rtnl_qdisc *qdisc,
    140 				struct nl_dump_params *p)
    141 {
    142 	struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
    143 
    144 	if (d == NULL)
    145 		return;
    146 
    147 	if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
    148 		nl_dump(p, " r2q %u", d->qh_rate2quantum);
    149 
    150 	if (d->qh_mask & SCH_HTB_HAS_DEFCLS) {
    151 		char buf[32];
    152 		nl_dump(p, " default %s",
    153 			rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf)));
    154 	}
    155 }
    156 
    157 static void htb_class_dump_line(struct rtnl_class *class,
    158 				struct nl_dump_params *p)
    159 {
    160 	struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
    161 
    162 	if (d == NULL)
    163 		return;
    164 
    165 	if (d->ch_mask & SCH_HTB_HAS_RATE) {
    166 		double r, rbit;
    167 		char *ru, *rubit;
    168 
    169 		r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru);
    170 		rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit);
    171 
    172 		nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
    173 			r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log);
    174 	}
    175 }
    176 
    177 static void htb_class_dump_details(struct rtnl_class *class,
    178 				   struct nl_dump_params *p)
    179 {
    180 	struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
    181 
    182 	if (d == NULL)
    183 		return;
    184 
    185 	/* line 1 */
    186 	if (d->ch_mask & SCH_HTB_HAS_CEIL) {
    187 		double r, rbit;
    188 		char *ru, *rubit;
    189 
    190 		r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru);
    191 		rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit);
    192 
    193 		nl_dump(p, "    ceil %.2f%s/s (%.0f%s) log %u",
    194 			r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log);
    195 	}
    196 
    197 	if (d->ch_mask & SCH_HTB_HAS_PRIO)
    198 		nl_dump(p, " prio %u", d->ch_prio);
    199 
    200 	if (d->ch_mask & SCH_HTB_HAS_MTU)
    201 		nl_dump(p, " mtu %u", d->ch_mtu);
    202 
    203 	if (d->ch_mask & SCH_HTB_HAS_RBUFFER) {
    204 		double b;
    205 		char *bu;
    206 
    207 		b = nl_cancel_down_bytes(d->ch_rbuffer, &bu);
    208 		nl_dump(p, " rbuffer %.2f%s", b, bu);
    209 	}
    210 
    211 	if (d->ch_mask & SCH_HTB_HAS_CBUFFER) {
    212 		double b;
    213 		char *bu;
    214 
    215 		b = nl_cancel_down_bytes(d->ch_cbuffer, &bu);
    216 		nl_dump(p, " cbuffer %.2f%s", b, bu);
    217 	}
    218 
    219 	if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
    220 		nl_dump(p, " quantum %u", d->ch_quantum);
    221 
    222 	if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
    223 		nl_dump(p, " overhead %u", d->ch_overhead);
    224 
    225 	if (d->ch_mask & SCH_HTB_HAS_MPU)
    226 		nl_dump(p, " mpu %u", d->ch_mpu);
    227 }
    228 
    229 static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc)
    230 {
    231 	struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
    232 	struct tc_htb_glob opts;
    233 	struct nl_msg *msg;
    234 
    235 	if (d == NULL)
    236 		return NULL;
    237 
    238 	msg = nlmsg_alloc();
    239 	if (msg == NULL)
    240 		return NULL;
    241 
    242 	memset(&opts, 0, sizeof(opts));
    243 	opts.version = TC_HTB_PROTOVER;
    244 
    245 	if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
    246 		opts.rate2quantum = d->qh_rate2quantum;
    247 	if (d->qh_mask & SCH_HTB_HAS_DEFCLS)
    248 		opts.defcls = d->qh_defcls;
    249 
    250 	nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
    251 
    252 	return msg;
    253 }
    254 
    255 static uint8_t compute_cell(uint32_t rate, uint32_t mtu)
    256 {
    257 	uint8_t cell_log = 0;
    258 	while (mtu > 255) {
    259 		mtu >>= 1;
    260 		cell_log++;
    261 	}
    262 
    263 	return cell_log;
    264 }
    265 
    266 static struct nl_msg *htb_class_get_opts(struct rtnl_class *class)
    267 {
    268 	struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
    269 	uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
    270 	struct tc_htb_opt opts;
    271 	struct nl_msg *msg;
    272 	int buffer, cbuffer;
    273 	uint8_t overhead = 0, mpu = 0;
    274 
    275 	if (d == NULL)
    276 		return NULL;
    277 
    278 	msg = nlmsg_alloc();
    279 	memset(&opts, 0, sizeof(opts));
    280 
    281 	/* if not set, zero (0) is used as priority */
    282 	if (d->ch_mask & SCH_HTB_HAS_PRIO)
    283 		opts.prio = d->ch_prio;
    284 
    285 	if (d->ch_mask & SCH_HTB_HAS_MTU)
    286 		mtu = d->ch_mtu;
    287 	else
    288 		mtu = 1600; /* eth packet len */
    289 
    290 	if (!(d->ch_mask & SCH_HTB_HAS_RATE))
    291 		BUG();
    292 
    293 	rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate);
    294 	/* if cell_log not set, compute default value */
    295 	if (opts.rate.cell_log == UINT8_MAX)
    296 		opts.rate.cell_log = compute_cell(opts.rate.rate, mtu);
    297 
    298 	/* if not set, configured rate is used as ceil, which implies no borrowing */
    299 	if (d->ch_mask & SCH_HTB_HAS_CEIL)
    300 		rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil);
    301 	else
    302 		memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
    303 	/* if cell_log not set, compute default value */
    304 	if (opts.ceil.cell_log == UINT8_MAX)
    305 		opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu);
    306 
    307 	if (d->ch_mask & SCH_HTB_HAS_RBUFFER)
    308 		buffer = d->ch_rbuffer;
    309 	else
    310 		buffer = opts.rate.rate / nl_get_hz() + mtu;
    311 
    312 	opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate);
    313 
    314 	if (d->ch_mask & SCH_HTB_HAS_CBUFFER)
    315 		cbuffer = d->ch_cbuffer;
    316 	else
    317 		cbuffer = opts.ceil.rate / nl_get_hz() + mtu;
    318 
    319 	opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate);
    320 
    321 	if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
    322 		opts.quantum = d->ch_quantum;
    323 
    324 	if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
    325 		overhead = d->ch_overhead;
    326 
    327 	if (d->ch_mask & SCH_HTB_HAS_MPU)
    328 		mpu = d->ch_mpu;
    329 
    330 	opts.rate.mpu = mpu | (overhead << 8);
    331 	opts.ceil.mpu = mpu | (overhead << 8);
    332 
    333 	nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
    334 
    335 	rtnl_tc_build_rate_table(rtable, mpu, overhead,
    336 				 1 << opts.rate.cell_log,
    337 				 opts.rate.rate);
    338 	nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
    339 
    340 	rtnl_tc_build_rate_table(ctable, mpu, overhead,
    341 				 1 << opts.ceil.cell_log,
    342 				 opts.ceil.rate);
    343 	nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
    344 
    345 	return msg;
    346 }
    347 
    348 /**
    349  * @name Attribute Modifications
    350  * @{
    351  */
    352 
    353 void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
    354 {
    355 	struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
    356 	if (d == NULL)
    357 		return;
    358 
    359 	d->qh_rate2quantum = rate2quantum;
    360 	d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
    361 }
    362 
    363 /**
    364  * Set default class of the htb qdisc to the specified value
    365  * @arg qdisc		qdisc to change
    366  * @arg defcls		new default class
    367  */
    368 void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
    369 {
    370 	struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
    371 	if (d == NULL)
    372 		return;
    373 
    374 	d->qh_defcls = defcls;
    375 	d->qh_mask |= SCH_HTB_HAS_DEFCLS;
    376 }
    377 
    378 void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
    379 {
    380 	struct rtnl_htb_class *d = htb_class(class);
    381 	if (d == NULL)
    382 		return;
    383 
    384 	d->ch_prio = prio;
    385 	d->ch_mask |= SCH_HTB_HAS_PRIO;
    386 }
    387 
    388 /**
    389  * Set MTU of the data link.
    390  * @arg class		HTB class to be modified.
    391  * @arg mtu		New MTU in bytes.
    392  *
    393  * Sets MTU of the data link controlled by the HTB class.
    394  * If not set, the Ethernet MTU (1600) is used.
    395  */
    396 void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu)
    397 {
    398 	struct rtnl_htb_class *d = htb_class(class);
    399 	if (d == NULL)
    400 		return;
    401 
    402 	d->ch_mtu = mtu;
    403 	d->ch_mask |= SCH_HTB_HAS_MTU;
    404 }
    405 
    406 /**
    407  * Set rate of HTB class.
    408  * @arg class		HTB class to be modified.
    409  * @arg rate		New rate in bytes per second.
    410  */
    411 void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
    412 {
    413 	struct rtnl_htb_class *d = htb_class(class);
    414 	if (d == NULL)
    415 		return;
    416 
    417 	d->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */
    418 	d->ch_rate.rs_rate = rate;
    419 	d->ch_mask |= SCH_HTB_HAS_RATE;
    420 }
    421 
    422 /**
    423  * Set ceil of HTB class.
    424  * @arg class		HTB class to be modified.
    425  * @arg ceil		New ceil in bytes per second.
    426  */
    427 void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
    428 {
    429 	struct rtnl_htb_class *d = htb_class(class);
    430 	if (d == NULL)
    431 		return;
    432 
    433 	d->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */
    434 	d->ch_ceil.rs_rate = ceil;
    435 	d->ch_mask |= SCH_HTB_HAS_CEIL;
    436 }
    437 
    438 /**
    439  * Set size of the rate bucket of HTB class.
    440  * @arg class		HTB class to be modified.
    441  * @arg rbuffer		New size in bytes.
    442  */
    443 void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
    444 {
    445 	struct rtnl_htb_class *d = htb_class(class);
    446 	if (d == NULL)
    447 		return;
    448 
    449 	d->ch_rbuffer = rbuffer;
    450 	d->ch_mask |= SCH_HTB_HAS_RBUFFER;
    451 }
    452 
    453 /**
    454  * Set size of the ceil bucket of HTB class.
    455  * @arg class		HTB class to be modified.
    456  * @arg cbuffer		New size in bytes.
    457  */
    458 void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
    459 {
    460 	struct rtnl_htb_class *d = htb_class(class);
    461 	if (d == NULL)
    462 		return;
    463 
    464 	d->ch_cbuffer = cbuffer;
    465 	d->ch_mask |= SCH_HTB_HAS_CBUFFER;
    466 }
    467 
    468 /**
    469  * Set how much bytes to serve from leaf at once of HTB class {use r2q}.
    470  * @arg class		HTB class to be modified.
    471  * @arg quantum		New size in bytes.
    472  */
    473 void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
    474 {
    475 	struct rtnl_htb_class *d = htb_class(class);
    476 	if (d == NULL)
    477 		return;
    478 
    479 	d->ch_quantum = quantum;
    480 	d->ch_mask |= SCH_HTB_HAS_QUANTUM;
    481 }
    482 
    483 /**
    484  * Set per-packet size overhead used in rate computations of HTB class.
    485  * @arg class		HTB class to be modified.
    486  * @arg overhead		Size in bytes.
    487  */
    488 void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead)
    489 {
    490 	struct rtnl_htb_class *d = htb_class(class);
    491 	if (d == NULL)
    492 		return;
    493 
    494 	d->ch_overhead = overhead;
    495 	d->ch_mask |= SCH_HTB_HAS_OVERHEAD;
    496 }
    497 
    498 /**
    499  * Set the minimum packet size used in rate computations of HTB class.
    500  * @arg class		HTB class to be modified.
    501  * @arg mpu		Size in bytes.
    502  */
    503 void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu)
    504 {
    505 	struct rtnl_htb_class *d = htb_class(class);
    506 	if (d == NULL)
    507 		return;
    508 
    509 	d->ch_mpu = mpu;
    510 	d->ch_mask |= SCH_HTB_HAS_MPU;
    511 }
    512 
    513 /** @} */
    514 
    515 static struct rtnl_qdisc_ops htb_qdisc_ops = {
    516 	.qo_kind		= "htb",
    517 	.qo_msg_parser		= htb_qdisc_msg_parser,
    518 	.qo_free_data		= htb_qdisc_free_data,
    519 	.qo_dump[NL_DUMP_LINE]	= htb_qdisc_dump_line,
    520 	.qo_get_opts		= htb_qdisc_get_opts,
    521 };
    522 
    523 static struct rtnl_class_ops htb_class_ops = {
    524 	.co_kind		= "htb",
    525 	.co_msg_parser		= htb_class_msg_parser,
    526 	.co_free_data		= htb_class_free_data,
    527 	.co_dump = {
    528 	    [NL_DUMP_LINE]	= htb_class_dump_line,
    529 	    [NL_DUMP_DETAILS]	= htb_class_dump_details,
    530 	},
    531 	.co_get_opts		= htb_class_get_opts,
    532 };
    533 
    534 static void __init htb_init(void)
    535 {
    536 	rtnl_qdisc_register(&htb_qdisc_ops);
    537 	rtnl_class_register(&htb_class_ops);
    538 }
    539 
    540 static void __exit htb_exit(void)
    541 {
    542 	rtnl_qdisc_unregister(&htb_qdisc_ops);
    543 	rtnl_class_unregister(&htb_class_ops);
    544 }
    545 
    546 /** @} */
    547