967 lines
25 KiB
Diff
Executable File
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);
|
|
+}
|
|
+
|
|
+/** @} */
|