htb.c

00001 /*
00002  * lib/route/qdisc/htb.c        HTB Qdisc
00003  *
00004  *      This library is free software; you can redistribute it and/or
00005  *      modify it under the terms of the GNU Lesser General Public
00006  *      License as published by the Free Software Foundation version 2.1
00007  *      of the License.
00008  *
00009  * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
00010  * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
00011  * Copyright (c) 2005-2006 Siemens AG Oesterreich
00012  */
00013 
00014 /**
00015  * @ingroup qdisc
00016  * @ingroup class
00017  * @defgroup qdisc_htb Hierachical Token Bucket (HTB)
00018  * @{
00019  */
00020 
00021 #include <netlink-local.h>
00022 #include <netlink-tc.h>
00023 #include <netlink/netlink.h>
00024 #include <netlink/cache.h>
00025 #include <netlink/utils.h>
00026 #include <netlink/route/tc-api.h>
00027 #include <netlink/route/qdisc.h>
00028 #include <netlink/route/class.h>
00029 #include <netlink/route/link.h>
00030 #include <netlink/route/qdisc/htb.h>
00031 
00032 /** @cond SKIP */
00033 #define SCH_HTB_HAS_RATE2QUANTUM        0x01
00034 #define SCH_HTB_HAS_DEFCLS              0x02
00035 
00036 #define SCH_HTB_HAS_PRIO                0x001
00037 #define SCH_HTB_HAS_RATE                0x002
00038 #define SCH_HTB_HAS_CEIL                0x004
00039 #define SCH_HTB_HAS_RBUFFER             0x008
00040 #define SCH_HTB_HAS_CBUFFER             0x010
00041 #define SCH_HTB_HAS_QUANTUM             0x020
00042 /** @endcond */
00043 
00044 static struct nla_policy htb_policy[TCA_HTB_MAX+1] = {
00045         [TCA_HTB_INIT]  = { .minlen = sizeof(struct tc_htb_glob) },
00046         [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) },
00047 };
00048 
00049 static int htb_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
00050 {
00051         struct nlattr *tb[TCA_HTB_MAX + 1];
00052         struct rtnl_htb_qdisc *htb = data;
00053         int err;
00054 
00055         if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0)
00056                 return err;
00057         
00058         if (tb[TCA_HTB_INIT]) {
00059                 struct tc_htb_glob opts;
00060 
00061                 nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts));
00062                 htb->qh_rate2quantum = opts.rate2quantum;
00063                 htb->qh_defcls = opts.defcls;
00064 
00065                 htb->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS);
00066         }
00067 
00068         return 0;
00069 }
00070 
00071 static int htb_class_msg_parser(struct rtnl_tc *tc, void *data)
00072 {
00073         struct nlattr *tb[TCA_HTB_MAX + 1];
00074         struct rtnl_htb_class *htb = data;
00075         int err;
00076 
00077         if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0)
00078                 return err;
00079         
00080         if (tb[TCA_HTB_PARMS]) {
00081                 struct tc_htb_opt opts;
00082 
00083                 nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts));
00084                 htb->ch_prio = opts.prio;
00085                 rtnl_copy_ratespec(&htb->ch_rate, &opts.rate);
00086                 rtnl_copy_ratespec(&htb->ch_ceil, &opts.ceil);
00087                 htb->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, opts.rate.rate);
00088                 htb->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, opts.ceil.rate);
00089                 htb->ch_quantum = opts.quantum;
00090 
00091                 rtnl_tc_set_mpu(tc, htb->ch_rate.rs_mpu);
00092                 rtnl_tc_set_overhead(tc, htb->ch_rate.rs_overhead);
00093 
00094                 htb->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE |
00095                                 SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER |
00096                                 SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM);
00097         }
00098 
00099         return 0;
00100 }
00101 
00102 static void htb_qdisc_dump_line(struct rtnl_tc *tc, void *data,
00103                                 struct nl_dump_params *p)
00104 {
00105         struct rtnl_htb_qdisc *htb = data;
00106 
00107         if (!htb)
00108                 return;
00109 
00110         if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
00111                 nl_dump(p, " r2q %u", htb->qh_rate2quantum);
00112 
00113         if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) {
00114                 char buf[32];
00115                 nl_dump(p, " default %s",
00116                         rtnl_tc_handle2str(htb->qh_defcls, buf, sizeof(buf)));
00117         }
00118 }
00119 
00120 static void htb_class_dump_line(struct rtnl_tc *tc, void *data,
00121                                 struct nl_dump_params *p)
00122 {
00123         struct rtnl_htb_class *htb = data;
00124 
00125         if (!htb)
00126                 return;
00127 
00128         if (htb->ch_mask & SCH_HTB_HAS_RATE) {
00129                 double r, rbit;
00130                 char *ru, *rubit;
00131 
00132                 r = nl_cancel_down_bytes(htb->ch_rate.rs_rate, &ru);
00133                 rbit = nl_cancel_down_bits(htb->ch_rate.rs_rate*8, &rubit);
00134 
00135                 nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
00136                         r, ru, rbit, rubit, 1<<htb->ch_rate.rs_cell_log);
00137         }
00138 }
00139 
00140 static void htb_class_dump_details(struct rtnl_tc *tc, void *data,
00141                                    struct nl_dump_params *p)
00142 {
00143         struct rtnl_htb_class *htb = data;
00144 
00145         if (!htb)
00146                 return;
00147 
00148         /* line 1 */
00149         if (htb->ch_mask & SCH_HTB_HAS_CEIL) {
00150                 double r, rbit;
00151                 char *ru, *rubit;
00152 
00153                 r = nl_cancel_down_bytes(htb->ch_ceil.rs_rate, &ru);
00154                 rbit = nl_cancel_down_bits(htb->ch_ceil.rs_rate*8, &rubit);
00155 
00156                 nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
00157                         r, ru, rbit, rubit, 1<<htb->ch_ceil.rs_cell_log);
00158         }
00159 
00160         if (htb->ch_mask & SCH_HTB_HAS_PRIO)
00161                 nl_dump(p, " prio %u", htb->ch_prio);
00162 
00163         if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) {
00164                 double b;
00165                 char *bu;
00166 
00167                 b = nl_cancel_down_bytes(htb->ch_rbuffer, &bu);
00168                 nl_dump(p, " rbuffer %.2f%s", b, bu);
00169         }
00170 
00171         if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) {
00172                 double b;
00173                 char *bu;
00174 
00175                 b = nl_cancel_down_bytes(htb->ch_cbuffer, &bu);
00176                 nl_dump(p, " cbuffer %.2f%s", b, bu);
00177         }
00178 
00179         if (htb->ch_mask & SCH_HTB_HAS_QUANTUM)
00180                 nl_dump(p, " quantum %u", htb->ch_quantum);
00181 }
00182 
00183 static int htb_qdisc_msg_fill(struct rtnl_tc *tc, void *data,
00184                               struct nl_msg *msg)
00185 {
00186         struct rtnl_htb_qdisc *htb = data;
00187         struct tc_htb_glob opts = {0};
00188 
00189         opts.version = TC_HTB_PROTOVER;
00190         opts.rate2quantum = 10;
00191 
00192         if (htb) {
00193                 if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
00194                         opts.rate2quantum = htb->qh_rate2quantum;
00195 
00196                 if (htb->qh_mask & SCH_HTB_HAS_DEFCLS)
00197                         opts.defcls = htb->qh_defcls;
00198         }
00199 
00200         return nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
00201 }
00202 
00203 static int htb_class_msg_fill(struct rtnl_tc *tc, void *data,
00204                               struct nl_msg *msg)
00205 {
00206         struct rtnl_htb_class *htb = data;
00207         uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
00208         struct tc_htb_opt opts;
00209         int buffer, cbuffer;
00210 
00211         if (!htb || !(htb->ch_mask & SCH_HTB_HAS_RATE))
00212                 BUG();
00213 
00214         /* if not set, zero (0) is used as priority */
00215         if (htb->ch_mask & SCH_HTB_HAS_PRIO)
00216                 opts.prio = htb->ch_prio;
00217 
00218         memset(&opts, 0, sizeof(opts));
00219 
00220         mtu = rtnl_tc_get_mtu(tc);
00221 
00222         rtnl_tc_build_rate_table(tc, &htb->ch_rate, rtable);
00223         rtnl_rcopy_ratespec(&opts.rate, &htb->ch_rate);
00224 
00225         if (htb->ch_mask & SCH_HTB_HAS_CEIL) {
00226                 rtnl_tc_build_rate_table(tc, &htb->ch_ceil, ctable);
00227                 rtnl_rcopy_ratespec(&opts.ceil, &htb->ch_ceil);
00228         } else {
00229                 /*
00230                  * If not set, configured rate is used as ceil, which implies
00231                  * no borrowing.
00232                  */
00233                 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
00234         }
00235 
00236         if (htb->ch_mask & SCH_HTB_HAS_RBUFFER)
00237                 buffer = htb->ch_rbuffer;
00238         else
00239                 buffer = opts.rate.rate / nl_get_user_hz() + mtu; /* XXX */
00240 
00241         opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate);
00242 
00243         if (htb->ch_mask & SCH_HTB_HAS_CBUFFER)
00244                 cbuffer = htb->ch_cbuffer;
00245         else
00246                 cbuffer = opts.ceil.rate / nl_get_user_hz() + mtu; /* XXX */
00247 
00248         opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate);
00249 
00250         if (htb->ch_mask & SCH_HTB_HAS_QUANTUM)
00251                 opts.quantum = htb->ch_quantum;
00252 
00253         NLA_PUT(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
00254         NLA_PUT(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
00255         NLA_PUT(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
00256 
00257         return 0;
00258 
00259 nla_put_failure:
00260         return -NLE_MSGSIZE;
00261 }
00262 
00263 /**
00264  * @name Attribute Modifications
00265  * @{
00266  */
00267 
00268 void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
00269 {
00270         struct rtnl_htb_qdisc *htb;
00271 
00272         if (!(htb = rtnl_tc_data(TC_CAST(qdisc))))
00273                 BUG();
00274 
00275         htb->qh_rate2quantum = rate2quantum;
00276         htb->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
00277 }
00278 
00279 /**
00280  * Set default class of the htb qdisc to the specified value
00281  * @arg qdisc           qdisc to change
00282  * @arg defcls          new default class
00283  */
00284 void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
00285 {
00286         struct rtnl_htb_qdisc *htb;
00287 
00288         if (!(htb = rtnl_tc_data(TC_CAST(qdisc))))
00289                 BUG();
00290 
00291         htb->qh_defcls = defcls;
00292         htb->qh_mask |= SCH_HTB_HAS_DEFCLS;
00293 }
00294 
00295 void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
00296 {
00297         struct rtnl_htb_class *htb;
00298 
00299         if (!(htb = rtnl_tc_data(TC_CAST(class))))
00300                 BUG();
00301 
00302         htb->ch_prio = prio;
00303         htb->ch_mask |= SCH_HTB_HAS_PRIO;
00304 }
00305 
00306 /**
00307  * Set rate of HTB class.
00308  * @arg class           HTB class to be modified.
00309  * @arg rate            New rate in bytes per second.
00310  */
00311 void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
00312 {
00313         struct rtnl_htb_class *htb;
00314 
00315         if (!(htb = rtnl_tc_data(TC_CAST(class))))
00316                 BUG();
00317 
00318         htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */
00319         htb->ch_rate.rs_rate = rate;
00320         htb->ch_mask |= SCH_HTB_HAS_RATE;
00321 }
00322 
00323 uint32_t rtnl_htb_get_rate(struct rtnl_class *class)
00324 {
00325         struct rtnl_htb_class *htb;
00326 
00327         if (!(htb = rtnl_tc_data(TC_CAST(class))))
00328                 return 0;
00329 
00330         return htb->ch_rate.rs_rate;
00331 }
00332 
00333 /**
00334  * Set ceil of HTB class.
00335  * @arg class           HTB class to be modified.
00336  * @arg ceil            New ceil in bytes per second.
00337  */
00338 void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
00339 {
00340         struct rtnl_htb_class *htb;
00341 
00342         if (!(htb = rtnl_tc_data(TC_CAST(class))))
00343                 BUG();
00344 
00345         htb->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */
00346         htb->ch_ceil.rs_rate = ceil;
00347         htb->ch_mask |= SCH_HTB_HAS_CEIL;
00348 }
00349 
00350 /**
00351  * Set size of the rate bucket of HTB class.
00352  * @arg class           HTB class to be modified.
00353  * @arg rbuffer         New size in bytes.
00354  */
00355 void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
00356 {
00357         struct rtnl_htb_class *htb;
00358 
00359         if (!(htb = rtnl_tc_data(TC_CAST(class))))
00360                 BUG();
00361 
00362         htb->ch_rbuffer = rbuffer;
00363         htb->ch_mask |= SCH_HTB_HAS_RBUFFER;
00364 }
00365 
00366 /**
00367  * Set size of the ceil bucket of HTB class.
00368  * @arg class           HTB class to be modified.
00369  * @arg cbuffer         New size in bytes.
00370  */
00371 void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
00372 {
00373         struct rtnl_htb_class *htb;
00374 
00375         if (!(htb = rtnl_tc_data(TC_CAST(class))))
00376                 BUG();
00377 
00378         htb->ch_cbuffer = cbuffer;
00379         htb->ch_mask |= SCH_HTB_HAS_CBUFFER;
00380 }
00381 
00382 /**
00383  * Set how much bytes to serve from leaf at once of HTB class {use r2q}.
00384  * @arg class           HTB class to be modified.
00385  * @arg quantum         New size in bytes.
00386  */
00387 void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
00388 {
00389         struct rtnl_htb_class *htb;
00390 
00391         if (!(htb = rtnl_tc_data(TC_CAST(class))))
00392                 BUG();
00393 
00394         htb->ch_quantum = quantum;
00395         htb->ch_mask |= SCH_HTB_HAS_QUANTUM;
00396 }
00397 
00398 /** @} */
00399 
00400 static struct rtnl_tc_ops htb_qdisc_ops = {
00401         .to_kind                = "htb",
00402         .to_type                = RTNL_TC_TYPE_QDISC,
00403         .to_size                = sizeof(struct rtnl_htb_qdisc),
00404         .to_msg_parser          = htb_qdisc_msg_parser,
00405         .to_dump[NL_DUMP_LINE]  = htb_qdisc_dump_line,
00406         .to_msg_fill            = htb_qdisc_msg_fill,
00407 };
00408 
00409 static struct rtnl_tc_ops htb_class_ops = {
00410         .to_kind                = "htb",
00411         .to_type                = RTNL_TC_TYPE_CLASS,
00412         .to_size                = sizeof(struct rtnl_htb_class),
00413         .to_msg_parser          = htb_class_msg_parser,
00414         .to_dump = {
00415             [NL_DUMP_LINE]      = htb_class_dump_line,
00416             [NL_DUMP_DETAILS]   = htb_class_dump_details,
00417         },
00418         .to_msg_fill            = htb_class_msg_fill,
00419 };
00420 
00421 static void __init htb_init(void)
00422 {
00423         rtnl_tc_register(&htb_qdisc_ops);
00424         rtnl_tc_register(&htb_class_ops);
00425 }
00426 
00427 static void __exit htb_exit(void)
00428 {
00429         rtnl_tc_unregister(&htb_qdisc_ops);
00430         rtnl_tc_unregister(&htb_class_ops);
00431 }
00432 
00433 /** @} */