1
1
Files
zyxel-vmg8825_b50b-cfw/package/libs/libnl/patches-3.2.21/0006-add-support-for-bf-qdisc.patch
T
2026-04-17 18:33:03 +02:00

967 lines
25 KiB
Diff
Executable File

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 <netlink/netlink.h>
+#include <netlink/route/tc.h>
+
+#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 <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/route/qdisc/bf.h>
+
+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 <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
+#include <netlink/route/link.h>
+#include <netlink/route/qdisc/bf.h>
+
+/** @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);
+}
+
+/** @} */