diff --git a/include/Makefile.am b/include/Makefile.am index 8df81eb..c5e0a90 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -54,6 +54,7 @@ nobase_libnlinclude_HEADERS = \ netlink/route/qdisc/red.h \ netlink/route/qdisc/sfq.h \ netlink/route/qdisc/tbf.h \ + netlink/route/qdisc/bf.h \ netlink/route/qdisc/hfsc.h \ netlink/route/qdisc/codel.h \ netlink/route/qdisc/fq_codel.h \ diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index a38e1b9..fdc441a 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -701,4 +701,59 @@ struct tc_fq_codel_xstats { struct tc_fq_codel_cl_stats class_stats; }; }; + + +/* BF section */ + +enum { + TCA_BF_UNSPEC = 0, + TCA_BF_PARAMS, + TCA_BF_INIT, + __TCA_BF_MAX, +}; + +enum TC_BF_STRATA { + TC_BF_STRATUM_RT = 0, + TC_BF_STRATUM_NOMINAL, + TC_BF_STRATUM_OPTIMAL, + TC_BF_STRATUM_BULK, + __TC_BF_STRATA_COUNT, +}; +#define TC_BF_STRATUM_NONE TC_BF_STRATUM_BULK + +#define TC_BF_MAX_PRIORITY_POW2 8 + +enum BF_PRIORITY_CALC { + BF_PRIORITY_CALC_DEFAULT = 0, + BF_PRIORITY_CALC_FLOW_NODE, + BF_PRIORITY_CALC_NODE_FLOW, + BF_PRIORITY_CALC_FLOW_ONLY, + BF_PRIORITY_CALC_NODE_ONLY, + __BF_PRIORITY_CALC_COUNT, +}; + +struct tc_bf_glob { + __u32 defcls; /* default class number */ + __u32 flow_priorities_pow2; /* Range of flow priorities log2 */ + __u32 node_priorities_pow2; /* Range of node priorities log2 */ + enum BF_PRIORITY_CALC calc; + __u32 total_bw; + __u32 direct_pkts; +}; + + +struct tc_bf_opt { + __u32 flow_priority; + __u32 node_priority; + __u32 bytes_per_sec_limits[__TC_BF_STRATA_COUNT]; +}; + +struct tc_bf_xstats { + enum TC_BF_STRATA oversub_strata; +}; + + +#define TCA_BF_MAX (__TCA_BF_MAX - 1) + + #endif diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h index de9382d..fe97fa4 100644 --- a/include/netlink-private/types.h +++ b/include/netlink-private/types.h @@ -616,6 +616,25 @@ struct rtnl_fq_codel_qdisc uint32_t qcq_mask; }; +struct rtnl_bf_qdisc +{ + uint32_t qb_defcls; + uint32_t qb_flow_priorities_pow2; + uint32_t qb_node_priorities_pow2; + enum BF_PRIORITY_CALC qb_calc_method; + uint32_t qb_total_bw; + uint32_t qb_direct_pkts; + uint32_t qb_mask; +}; + +struct rtnl_bf_class +{ + uint32_t cb_flow_prio; + uint32_t cb_node_prio; + uint32_t cb_limits[__TC_BF_STRATA_COUNT]; + uint32_t cb_mask; +}; + struct rtnl_sfq { uint32_t qs_quantum; diff --git a/include/netlink/route/qdisc/bf.h b/include/netlink/route/qdisc/bf.h new file mode 100644 index 0000000..c5558ef --- /dev/null +++ b/include/netlink/route/qdisc/bf.h @@ -0,0 +1,53 @@ +/* + * netlink/route/sch/bf.h BF Qdisc + */ +/* + ************************************************************************** + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all copies. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + ************************************************************************** + */ + +#ifndef NETLINK_BF_H_ +#define NETLINK_BF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint32_t rtnl_bf_get_defcls(struct rtnl_qdisc *); +extern int rtnl_bf_set_defcls(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_bf_get_flow_priorities(struct rtnl_qdisc *); +extern uint32_t rtnl_bf_get_node_priorities(struct rtnl_qdisc *); +extern int rtnl_bf_set_priorities(struct rtnl_qdisc *, uint32_t flow_prios, + uint32_t node_prios); +extern enum BF_PRIORITY_CALC rtnl_bf_get_prio_calc_method(struct rtnl_qdisc *); +extern int rtnl_bf_set_prio_calc_method(struct rtnl_qdisc *, + enum BF_PRIORITY_CALC); +extern uint32_t rtnl_bf_get_total_bandwidth(struct rtnl_qdisc *); +extern int rtnl_bf_set_total_bandwidth(struct rtnl_qdisc *, uint32_t); + +extern uint32_t rtnl_bf_get_flow_prio(struct rtnl_class *); +extern int rtnl_bf_set_flow_prio(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_bf_get_node_prio(struct rtnl_class *); +extern int rtnl_bf_set_node_prio(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_bf_get_rate(struct rtnl_class *, uint32_t); +extern int rtnl_bf_set_rates(struct rtnl_class *, uint32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index a1b5ddb..c6f5639 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -80,6 +80,7 @@ libnl_route_3_la_SOURCES = \ route/qdisc/hfsc.c \ route/qdisc/drr.c \ route/qdisc/sfb.c \ + route/qdisc/bf.c \ \ fib_lookup/lookup.c fib_lookup/request.c \ \ @@ -106,6 +107,7 @@ EXTRA_DIST = \ if ENABLE_CLI nobase_pkglib_LTLIBRARIES = \ cli/qdisc/htb.la \ + cli/qdisc/bf.la \ cli/qdisc/hfsc.la \ cli/qdisc/blackhole.la \ cli/qdisc/pfifo.la \ @@ -115,6 +117,7 @@ nobase_pkglib_LTLIBRARIES = \ cli/cls/cgroup.la cli_qdisc_htb_la_LDFLAGS = -module -avoid-version +cli_qdisc_bf_la_LDFLAGS = -module -avoid-version cli_qdisc_hfsc_la_LDFLAGS = -module -avoid-version cli_qdisc_blackhole_la_LDFLAGS = -module -avoid-version cli_qdisc_pfifo_la_LDFLAGS = -module -avoid-version diff --git a/lib/cli/qdisc/bf.c b/lib/cli/qdisc/bf.c new file mode 100644 index 0000000..8b83b07 --- /dev/null +++ b/lib/cli/qdisc/bf.c @@ -0,0 +1,218 @@ +/* + * lib/cli/qdisc/bf.c Bigfoot module for CLI lib + */ +/* + ************************************************************************** + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all copies. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + ************************************************************************** + */ + +#include +#include +#include + +static void print_qdisc_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] bf [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --default=ID Default class for unclassified traffic.\n" +" --flow-prios=FPRIOS Range from 1 to NPRIOS available for flows.\n" +" --node-prios=NPRIOS Range from 1 to FPRIOS available for nodes.\n" +" --calc-method=METHOD ID of calc method for combining node/flow prios.\n" +" --total-bw=BYTES/SEC Total available bandwidth in bytes per second.\n" +"\n" +"CALC METHOD\n" +" The available methods are:\n" +" 0 Default This is the FLOW/NODE method\n" +" 1 Flow/Node Strict priority with flow priority precedence.\n" +" 2 Node/Flow Strict priority with node priority precedence.\n" +" 3 Flow Only Strict priority, flow only, node not considered.\n" +" 4 Node Only Strict priority, node only, flow not considered.\n" +"NOTE\n" +" Due to internal limitations, 0 is not a valid value for either FPRIOS or\n" +" NPRIOS. " +"EXAMPLE\n" +" # Create bf root qdisc 1: and direct unclassified traffic to class 1:10\n" +" nl-qdisc-add --dev=eth1 --parent=root --handle=1: bf --default=10 --flow-prios=4 --node-prios=16 --calc-method=0 --total-bw=1000000\n"); +} + +static void bf_parse_qdisc_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + unsigned int flow_prios = 0; + unsigned int node_prios = 0; + long rate; + + for (;;) { + int c, optidx = 0; + enum { + ARG_DEFAULT = 257, + ARG_FLOW_PRIOS = 258, + ARG_NODE_PRIOS = 259, + ARG_CALC_METHOD = 260, + ARG_TOTAL_BW = 261, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "default", 1, 0, ARG_DEFAULT }, + { "flow-prios", 1, 0, ARG_FLOW_PRIOS }, + { "node-prios", 1, 0, ARG_NODE_PRIOS }, + { "calc-method", 1, 0, ARG_CALC_METHOD }, + { "total-bw", 1, 0, ARG_TOTAL_BW }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hv", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_qdisc_usage(); + return; + case ARG_DEFAULT: + rtnl_bf_set_defcls(qdisc, nl_cli_parse_u32(optarg)); + break; + case ARG_FLOW_PRIOS: + flow_prios = nl_cli_parse_u32(optarg); + break; + case ARG_NODE_PRIOS: + node_prios = nl_cli_parse_u32(optarg); + break; + case ARG_CALC_METHOD: + rtnl_bf_set_prio_calc_method(qdisc, + nl_cli_parse_u32(optarg)); + break; + case ARG_TOTAL_BW: + rate = nl_size2int(optarg); + if (rate < 0) { + nl_cli_fatal(rate, "Unable to parse total-bw " + "\"%s\": Invalid format.", optarg); + } + rtnl_bf_set_total_bandwidth(qdisc, rate); + break; + + } + } + + if ((flow_prios != 0) || (node_prios != 0)) + rtnl_bf_set_priorities(qdisc, flow_prios, node_prios); +} + +static void print_class_usage(void) +{ + printf( +"Usage: nl-class-add [...] bf [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --realtime=RATE Realtime rate limit (default: 0).\n" +" --nominal=RATE Nominal rate limit (default: rt rate).\n" +" --optimal=RATE Optimal rate limit (default: nom rate).\n" +" --flow-prio=FPRIO App Priority, lower is served first (default: 1).\n" +" --node-prio=NPRIO Device Priority, lower is first (default: 1).\n" +" --total-bw=BW Total BW available for the interface (default: 0)\n" +"\n" +"EXAMPLE" +" # Attach class 1:1 to bf qdisc 1: and nominal rate limit it to 20mbit\n" +" nl-class-add --dev=eth1 --parent=1: --classid=1:1 bf --nom=20mbit\n"); +} + +static void bf_parse_class_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_class *cls = (struct rtnl_class *) tc; + unsigned int rates[__TC_BF_STRATA_COUNT] = {0}; + + for (;;) { + int c, optidx = 0; + enum { + ARG_RT = 257, + ARG_NOM = 258, + ARG_OPT = 259, + ARG_FPRIO = 260, + ARG_NPRIO = 261, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "realtime", 1, 0, ARG_RT }, + { "nominal", 1, 0, ARG_NOM }, + { "optimal", 1, 0, ARG_OPT }, + { "flow-prio", 1, 0, ARG_FPRIO }, + { "node-prio", 1, 0, ARG_NPRIO }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_class_usage(); + return; + case ARG_RT: + rates[TC_BF_STRATUM_RT] = nl_cli_parse_u32(optarg); + break; + case ARG_NOM: + rates[TC_BF_STRATUM_NOMINAL] = nl_cli_parse_u32(optarg); + break; + case ARG_OPT: + rates[TC_BF_STRATUM_OPTIMAL] = nl_cli_parse_u32(optarg); + break; + case ARG_FPRIO: + rtnl_bf_set_flow_prio(cls, nl_cli_parse_u32(optarg)); + break; + case ARG_NPRIO: + rtnl_bf_set_node_prio(cls, nl_cli_parse_u32(optarg)); + break; + + } + } + + if (rates[TC_BF_STRATUM_NOMINAL] == 0) + rates[TC_BF_STRATUM_NOMINAL] = rates[TC_BF_STRATUM_RT]; + + if (rates[TC_BF_STRATUM_OPTIMAL] == 0) + rates[TC_BF_STRATUM_OPTIMAL] = rates[TC_BF_STRATUM_NOMINAL]; + + rtnl_bf_set_rates(cls, rates); +} + +static struct nl_cli_tc_module bf_qdisc_module = +{ + .tm_name = "bf", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = bf_parse_qdisc_argv, +}; + +static struct nl_cli_tc_module bf_class_module = +{ + .tm_name = "bf", + .tm_type = RTNL_TC_TYPE_CLASS, + .tm_parse_argv = bf_parse_class_argv, +}; + +static void __init bf_init(void) +{ + nl_cli_tc_register(&bf_qdisc_module); + nl_cli_tc_register(&bf_class_module); +} + +static void __exit bf_exit(void) +{ + nl_cli_tc_unregister(&bf_class_module); + nl_cli_tc_unregister(&bf_qdisc_module); +} diff --git a/lib/route/qdisc/bf.c b/lib/route/qdisc/bf.c new file mode 100644 index 0000000..845ec88 --- /dev/null +++ b/lib/route/qdisc/bf.c @@ -0,0 +1,543 @@ +/* + * lib/route/qdisc/bf.c BF Qdisc + */ +/* + ************************************************************************** + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all copies. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + ************************************************************************** + */ + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_bf Bigfoot Scheduler (BF) + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_BF_HAS_DEFCLS 0x001 +#define SCH_BF_HAS_FLOW_PRIO_LIMIT 0x002 +#define SCH_BF_HAS_NODE_PRIO_LIMIT 0x004 +#define SCH_BF_HAS_PRIO_CALC_METHOD 0x008 +#define SCH_BF_HAS_TOTAL_BW 0x010 +#define SCH_BF_HAS_FLOW_PRIO 0x020 +#define SCH_BF_HAS_NODE_PRIO 0x040 +#define SCH_BF_HAS_RATES 0x080 +/** @endcond */ + +static char *strata_names[__TC_BF_STRATA_COUNT] = { + "realtime", "nominal", "optimal", "bulk", +}; + +static char *prio_calc_method_names[__BF_PRIORITY_CALC_COUNT] = { + "default", "flow-node", "node-flow", "flow-only", "node-only", +}; + +static struct nla_policy bf_policy[TCA_BF_MAX+1] = { + [TCA_BF_INIT] = { .minlen = sizeof(struct tc_bf_glob) }, + [TCA_BF_PARAMS] = { .minlen = sizeof(struct tc_bf_opt) }, +}; + +static int bf_qdisc_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_BF_MAX + 1]; + struct rtnl_bf_qdisc *bf = data; + int err; + + if ((err = tca_parse(tb, TCA_BF_MAX, tc, bf_policy)) < 0) + return err; + + if (tb[TCA_BF_INIT]) { + struct tc_bf_glob opts; + + bf->qb_mask = 0; + nla_memcpy(&opts, tb[TCA_BF_INIT], sizeof(opts)); + + bf->qb_defcls = opts.defcls; + bf->qb_mask |= SCH_BF_HAS_DEFCLS; + + bf->qb_flow_priorities_pow2 = opts.flow_priorities_pow2; + bf->qb_mask |= SCH_BF_HAS_FLOW_PRIO_LIMIT; + + bf->qb_node_priorities_pow2 = opts.node_priorities_pow2; + bf->qb_mask |= SCH_BF_HAS_NODE_PRIO_LIMIT; + + bf->qb_calc_method = opts.calc; + bf->qb_mask |= SCH_BF_HAS_PRIO_CALC_METHOD; + + bf->qb_total_bw = opts.total_bw; + bf->qb_mask |= SCH_BF_HAS_TOTAL_BW; + } + + return 0; +} + +static int bf_class_msg_parser(struct rtnl_tc *tc, void *data) +{ + int err; + struct nlattr *tb[TCA_BF_MAX + 1]; + struct rtnl_bf_class *bf = data; + + if ((err = tca_parse(tb, TCA_BF_MAX, tc, bf_policy)) < 0) + return err; + + if (tb[TCA_BF_PARAMS]) { + unsigned int i; + struct tc_bf_opt opts; + + bf->cb_mask = 0; + nla_memcpy(&opts, tb[TCA_BF_PARAMS], sizeof(opts)); + bf->cb_flow_prio = opts.flow_priority; + bf->cb_mask |= SCH_BF_HAS_FLOW_PRIO; + + bf->cb_node_prio = opts.node_priority; + bf->cb_mask |= SCH_BF_HAS_NODE_PRIO; + + for (i = 0; i < __TC_BF_STRATA_COUNT; i++) { + bf->cb_limits[i] = opts.bytes_per_sec_limits[i]; + } + bf->cb_mask |= SCH_BF_HAS_RATES; + } + + return 0; +} + +static void bf_qdisc_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_bf_qdisc *bf = data; + + if (!bf) + return; + + if (bf->qb_mask & SCH_BF_HAS_DEFCLS) { + char buf[64]; + nl_dump(p, " default-class %s", + rtnl_tc_handle2str(bf->qb_defcls, + buf, sizeof(buf))); + } + + if (bf->qb_mask & SCH_BF_HAS_FLOW_PRIO_LIMIT) { + nl_dump(p, " flow-priorities %u", + (1 << bf->qb_flow_priorities_pow2)); + } + + if (bf->qb_mask & SCH_BF_HAS_NODE_PRIO_LIMIT) { + nl_dump(p, " node-priorities %u", + (1 << bf->qb_node_priorities_pow2)); + } + + if (bf->qb_mask & SCH_BF_HAS_PRIO_CALC_METHOD) { + nl_dump(p, " priority-calculation %s", + prio_calc_method_names[bf->qb_calc_method]); + } + + if (bf->qb_mask & SCH_BF_HAS_TOTAL_BW) { + nl_dump(p, " total-bw %u", bf->qb_total_bw); + } +} + +static void bf_class_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_bf_class *bf = data; + + if (!bf) + return; + + if (bf->cb_mask & SCH_BF_HAS_FLOW_PRIO) { + nl_dump(p, " flow_prio %u", bf->cb_node_prio); + } + + if (bf->cb_mask & SCH_BF_HAS_NODE_PRIO) { + nl_dump(p, " node_prio %u", bf->cb_node_prio); + } + + if (bf->cb_mask & SCH_BF_HAS_RATES) { + unsigned int i; + + for (i = 0; i < __TC_BF_STRATA_COUNT; i++) { + nl_dump(p, " %s_limit %u", strata_names[i], + bf->cb_limits[i]); + } + } +} + +static void bf_class_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + enum TC_BF_STRATA stratum; + struct tc_bf_xstats *x; + + if (!(x = tca_xstats(tc))) + return; + + stratum = x->oversub_strata; + if (stratum == TC_BF_STRATUM_BULK) { + nl_dump(p, " No strata"); + } else { + while(stratum < TC_BF_STRATUM_BULK) { + nl_dump(p, " %s", strata_names[stratum]); + stratum++; + } + } + nl_dump_line(p, " limited (oversubscription)\n"); +} + +static int bf_qdisc_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_bf_qdisc *bf = data; + struct tc_bf_glob opts = {0}; + + if (bf) { + if (bf->qb_mask & SCH_BF_HAS_DEFCLS) + opts.defcls = bf->qb_defcls; + + if (bf->qb_mask & SCH_BF_HAS_FLOW_PRIO_LIMIT) + opts.flow_priorities_pow2 = bf->qb_flow_priorities_pow2; + + if (bf->qb_mask & SCH_BF_HAS_NODE_PRIO_LIMIT) + opts.node_priorities_pow2 = bf->qb_node_priorities_pow2; + + if (bf->qb_mask & SCH_BF_HAS_PRIO_CALC_METHOD) + opts.calc = bf->qb_calc_method; + + if (bf->qb_mask & SCH_BF_HAS_TOTAL_BW) + opts.total_bw = bf->qb_total_bw; + } + + return nla_put(msg, TCA_BF_INIT, sizeof(opts), &opts); +} + + +static int bf_class_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + unsigned int i; + struct rtnl_bf_class *bf = data; + struct tc_bf_opt opts = {0}; + + if ((!bf) || !(bf->cb_mask & (SCH_BF_HAS_RATES))) + BUG(); + + if (bf->cb_mask & SCH_BF_HAS_FLOW_PRIO) { + opts.flow_priority = bf->cb_flow_prio; + } + + if (bf->cb_mask & SCH_BF_HAS_NODE_PRIO) { + opts.node_priority = bf->cb_node_prio; + } + + for (i = 0; i < __TC_BF_STRATA_COUNT; i++) { + opts.bytes_per_sec_limits[i] = bf->cb_limits[i]; + } + + NLA_PUT(msg, TCA_BF_PARAMS, sizeof(opts), &opts); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static struct rtnl_tc_ops bf_qdisc_ops; +static struct rtnl_tc_ops bf_class_ops; + +static struct rtnl_bf_qdisc *bf_qdisc_data(struct rtnl_qdisc *qdisc) +{ + return rtnl_tc_data_check(TC_CAST(qdisc), &bf_qdisc_ops); +} + +static struct rtnl_bf_class *bf_class_data(struct rtnl_class *class) +{ + return rtnl_tc_data_check(TC_CAST(class), &bf_class_ops); +} + +/* + * Calculate the next highest power of 2. 0 is an illegal value to pass to + * this function. + */ +static unsigned int next_pow2(unsigned int value) +{ + unsigned int retval; + unsigned int bits; + + if (value == 0) + return 0; + + bits = 0; + retval = 0; + do { + retval++; + bits += value & 1; + value >>= 1; + } while (value != 0); + + return (bits > 1) ? retval : retval - 1; +} + + + +/** + * @name Attribute Modifications + * @{ + */ +uint32_t rtnl_bf_get_defcls(struct rtnl_qdisc *qdisc) +{ + struct rtnl_bf_qdisc *bf; + + if ((bf = bf_qdisc_data(qdisc)) && bf->qb_mask & SCH_BF_HAS_DEFCLS) + return bf->qb_defcls; + + return TC_H_UNSPEC; +} + + +int rtnl_bf_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) +{ + struct rtnl_bf_qdisc *bf; + + if (!(bf = bf_qdisc_data(qdisc))) + return -NLE_OPNOTSUPP; + + bf->qb_defcls = defcls; + bf->qb_mask |= SCH_BF_HAS_DEFCLS; + + return 0; +} + +uint32_t rtnl_bf_get_flow_priorities(struct rtnl_qdisc *qdisc) +{ + struct rtnl_bf_qdisc *bf; + + if ((bf = bf_qdisc_data(qdisc)) && + (bf->qb_mask & SCH_BF_HAS_FLOW_PRIO_LIMIT)) + return (1 << bf->qb_flow_priorities_pow2); + + return TC_H_UNSPEC; +} + +uint32_t rtnl_bf_get_node_priorities(struct rtnl_qdisc *qdisc) +{ + struct rtnl_bf_qdisc *bf; + + if ((bf = bf_qdisc_data(qdisc)) && + (bf->qb_mask & SCH_BF_HAS_NODE_PRIO_LIMIT)) + return (1 << bf->qb_node_priorities_pow2); + + return TC_H_UNSPEC; +} + +int rtnl_bf_set_priorities(struct rtnl_qdisc *qdisc, uint32_t flow_prios, + uint32_t node_prios) +{ + unsigned int flow_pow2, node_pow2; + struct rtnl_bf_qdisc *bf; + + if (!(bf = bf_qdisc_data(qdisc))) + return -NLE_OPNOTSUPP; + + if ((flow_prios == 0) || (node_prios == 0)) + return -NLE_RANGE; + + flow_pow2 = next_pow2(flow_prios); + node_pow2 = next_pow2(node_prios); + + if ((flow_pow2 + node_pow2) > TC_BF_MAX_PRIORITY_POW2) + return -NLE_RANGE; + + bf->qb_node_priorities_pow2 = node_pow2; + bf->qb_flow_priorities_pow2 = flow_pow2; + + bf->qb_mask |= SCH_BF_HAS_NODE_PRIO_LIMIT; + bf->qb_mask |= SCH_BF_HAS_FLOW_PRIO_LIMIT; + + return 0; +} + +enum BF_PRIORITY_CALC rtnl_bf_get_prio_calc_method(struct rtnl_qdisc *qdisc) +{ + struct rtnl_bf_qdisc *bf; + + if ((bf = bf_qdisc_data(qdisc)) && + (bf->qb_mask & SCH_BF_HAS_PRIO_CALC_METHOD)) + return bf->qb_calc_method; + + return TC_H_UNSPEC; +} + +int rtnl_bf_set_prio_calc_method(struct rtnl_qdisc *qdisc, + enum BF_PRIORITY_CALC calc) +{ + struct rtnl_bf_qdisc *bf; + + if (!(bf = bf_qdisc_data(qdisc))) + return -NLE_OPNOTSUPP; + + bf->qb_calc_method = calc; + bf->qb_mask |= SCH_BF_HAS_PRIO_CALC_METHOD; + + return 0; +} + +uint32_t rtnl_bf_get_total_bandwidth(struct rtnl_qdisc *qdisc) +{ + struct rtnl_bf_qdisc *bf; + + if ((bf = bf_qdisc_data(qdisc)) && + (bf->qb_mask & SCH_BF_HAS_TOTAL_BW)) + return bf->qb_total_bw; + + return TC_H_UNSPEC; +} + +int rtnl_bf_set_total_bandwidth(struct rtnl_qdisc *qdisc, uint32_t total_bw) +{ + struct rtnl_bf_qdisc *bf; + + if (!(bf = bf_qdisc_data(qdisc))) + return -NLE_OPNOTSUPP; + + bf->qb_total_bw = total_bw; + bf->qb_mask |= SCH_BF_HAS_TOTAL_BW; + + return 0; +} + + +uint32_t rtnl_bf_get_flow_prio(struct rtnl_class *cls) +{ + struct rtnl_bf_class *bf; + + if ((bf = bf_class_data(cls)) && bf->cb_mask & SCH_BF_HAS_FLOW_PRIO) + return bf->cb_flow_prio; + + return 0; +} + +int rtnl_bf_set_flow_prio(struct rtnl_class *class, uint32_t flow_prio) +{ + struct rtnl_bf_class *bf; + + if (!(bf = bf_class_data(class))) + return -NLE_OPNOTSUPP; + + bf->cb_flow_prio = flow_prio; + bf->cb_mask |= SCH_BF_HAS_FLOW_PRIO; + + return 0; +} + +uint32_t rtnl_bf_get_node_prio(struct rtnl_class *class) +{ + struct rtnl_bf_class *bf; + + if ((bf = bf_class_data(class)) && bf->cb_mask & SCH_BF_HAS_NODE_PRIO) + return bf->cb_node_prio; + + return 0; +} + +int rtnl_bf_set_node_prio(struct rtnl_class *class, uint32_t node_prio) +{ + struct rtnl_bf_class *bf; + + if (!(bf = bf_class_data(class))) + return -NLE_OPNOTSUPP; + + bf->cb_node_prio = node_prio; + bf->cb_mask |= SCH_BF_HAS_NODE_PRIO; + + return 0; +} + + +uint32_t rtnl_bf_get_rate(struct rtnl_class *class, uint32_t strata) +{ + struct rtnl_bf_class *bf; + + if ((bf = bf_class_data(class)) && bf->cb_mask & SCH_BF_HAS_RATES) + return bf->cb_limits[strata]; + + return 0; +} + +int rtnl_bf_set_rates(struct rtnl_class *class, uint32_t *rates) +{ + unsigned int i; + struct rtnl_bf_class *bf; + + if (!(bf = bf_class_data(class))) + return -NLE_OPNOTSUPP; + + if (!rates) + return -ENOENT; + + for (i = 0; i < __TC_BF_STRATA_COUNT; i++) { + bf->cb_limits[i] = rates[i]; + } + bf->cb_mask |= SCH_BF_HAS_RATES; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops bf_qdisc_ops = { + .to_kind = "bf", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_bf_qdisc), + .to_msg_parser = bf_qdisc_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = bf_qdisc_dump_line, + [NL_DUMP_DETAILS] = NULL, + }, + .to_msg_fill = bf_qdisc_msg_fill, +}; + +static struct rtnl_tc_ops bf_class_ops = { + .to_kind = "bf", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_bf_class), + .to_msg_parser = bf_class_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = bf_class_dump_line, + [NL_DUMP_DETAILS] = NULL, + [NL_DUMP_STATS] = bf_class_dump_stats, + }, + .to_msg_fill = bf_class_msg_fill, +}; + +static void __init bf_init(void) +{ + rtnl_tc_register(&bf_qdisc_ops); + rtnl_tc_register(&bf_class_ops); +} + +static void __exit bf_exit(void) +{ + rtnl_tc_unregister(&bf_class_ops); + rtnl_tc_unregister(&bf_qdisc_ops); +} + +/** @} */