762 lines
20 KiB
Diff
Executable File
762 lines
20 KiB
Diff
Executable File
diff --git a/include/Makefile.am b/include/Makefile.am
|
|
index 3f96690..79206fd 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/hfsc.h \
|
|
netlink/route/qdisc/fq_codel.h \
|
|
netlink/route/qdisc/plug.h \
|
|
netlink/route/addr.h \
|
|
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
|
|
index d6d056d..ba21a7f 100644
|
|
--- a/include/netlink-private/types.h
|
|
+++ b/include/netlink-private/types.h
|
|
@@ -651,6 +651,27 @@ struct rtnl_netem
|
|
struct rtnl_netem_dist qnm_dist;
|
|
};
|
|
|
|
+struct rtnl_hfsc_qdisc
|
|
+{
|
|
+ uint32_t qsc_defcls;
|
|
+ uint32_t qsc_mask;
|
|
+};
|
|
+
|
|
+struct rtnl_curve
|
|
+{
|
|
+ uint32_t sc_m1;
|
|
+ uint32_t sc_d;
|
|
+ uint32_t sc_m2;
|
|
+};
|
|
+
|
|
+struct rtnl_hfsc_class
|
|
+{
|
|
+ struct rtnl_curve csc_rt_sc;
|
|
+ struct rtnl_curve csc_fair_sc;
|
|
+ struct rtnl_curve csc_upper_sc;
|
|
+ uint32_t csc_mask;
|
|
+};
|
|
+
|
|
struct rtnl_htb_qdisc
|
|
{
|
|
uint32_t qh_rate2quantum;
|
|
diff --git a/include/netlink/route/qdisc/hfsc.h b/include/netlink/route/qdisc/hfsc.h
|
|
new file mode 100644
|
|
index 0000000..d47b29d
|
|
--- /dev/null
|
|
+++ b/include/netlink/route/qdisc/hfsc.h
|
|
@@ -0,0 +1,46 @@
|
|
+/*
|
|
+ * netlink/route/sch/hfsc.h HFSC Qdisc
|
|
+ */
|
|
+
|
|
+#ifndef NETLINK_HFSC_H_
|
|
+#define NETLINK_HFSC_H_
|
|
+
|
|
+#include <netlink/netlink.h>
|
|
+#include <netlink/route/tc.h>
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+struct hfsc_spec {
|
|
+ unsigned int rate;
|
|
+ unsigned int delay_max;
|
|
+ unsigned int work_max;
|
|
+};
|
|
+
|
|
+extern uint32_t rtnl_hfsc_get_defcls(struct rtnl_qdisc *);
|
|
+extern int rtnl_hfsc_set_defcls(struct rtnl_qdisc *, uint32_t);
|
|
+
|
|
+/* m1-d-m2: Direct Method of Specifying service curve slopes */
|
|
+extern void rtnl_hfsc_get_realtime_sc(struct rtnl_class *,
|
|
+ struct tc_service_curve *);
|
|
+extern int rtnl_hfsc_set_realtime_sc(struct rtnl_class *,
|
|
+ struct tc_service_curve *);
|
|
+extern void rtnl_hfsc_get_fair_sc(struct rtnl_class *,
|
|
+ struct tc_service_curve *);
|
|
+extern int rtnl_hfsc_set_fair_sc(struct rtnl_class *,
|
|
+ struct tc_service_curve *);
|
|
+extern void rtnl_hfsc_get_upperlimit_sc(struct rtnl_class *,
|
|
+ struct tc_service_curve *);
|
|
+extern int rtnl_hfsc_set_upperlimit_sc(struct rtnl_class *,
|
|
+ struct tc_service_curve *);
|
|
+
|
|
+/* umax-dmax-rate Method of specifying service curve translations */
|
|
+extern int rtnl_hfsc_spec_to_sc(struct hfsc_spec *,
|
|
+ struct tc_service_curve *);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
diff --git a/lib/Makefile.am b/lib/Makefile.am
|
|
index 1dce098..e715ab7 100644
|
|
--- a/lib/Makefile.am
|
|
+++ b/lib/Makefile.am
|
|
@@ -76,6 +76,7 @@ libnl_route_3_la_SOURCES = \
|
|
route/qdisc/prio.c route/qdisc/red.c route/qdisc/sfq.c \
|
|
route/qdisc/tbf.c route/qdisc/plug.c \
|
|
route/qdisc/fq_codel.c \
|
|
+ route/qdisc/hfsc.c \
|
|
\
|
|
fib_lookup/lookup.c fib_lookup/request.c \
|
|
\
|
|
@@ -102,6 +103,7 @@ EXTRA_DIST = \
|
|
if ENABLE_CLI
|
|
nobase_pkglib_LTLIBRARIES = \
|
|
cli/qdisc/htb.la \
|
|
+ cli/qdisc/hfsc.la \
|
|
cli/qdisc/blackhole.la \
|
|
cli/qdisc/pfifo.la \
|
|
cli/qdisc/plug.la \
|
|
@@ -110,6 +112,7 @@ nobase_pkglib_LTLIBRARIES = \
|
|
cli/cls/cgroup.la
|
|
|
|
cli_qdisc_htb_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
|
|
cli_qdisc_plug_la_LDFLAGS = -module -avoid-version
|
|
diff --git a/lib/cli/qdisc/hfsc.c b/lib/cli/qdisc/hfsc.c
|
|
new file mode 100644
|
|
index 0000000..1c0f713
|
|
--- /dev/null
|
|
+++ b/lib/cli/qdisc/hfsc.c
|
|
@@ -0,0 +1,221 @@
|
|
+/*
|
|
+ * lib/cli/qdisc/hfsc.c HFSC 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/hfsc.h>
|
|
+
|
|
+static void print_qdisc_usage(void)
|
|
+{
|
|
+ printf(
|
|
+"Usage: nl-qdisc-add [...] hfsc [OPTIONS]...\n"
|
|
+"\n"
|
|
+"OPTIONS\n"
|
|
+" --help Show this help text.\n"
|
|
+" --default=ID Default class for unclassified traffic.\n"
|
|
+"\n"
|
|
+"EXAMPLE"
|
|
+" # Create hfsc root qdisc 1: and direct unclassified traffic to class 1:3\n"
|
|
+" nl-qdisc-add --dev=eth1 --parent=root --handle=1: hfsc --default=3\n");
|
|
+}
|
|
+
|
|
+static void hfsc_parse_qdisc_argv(struct rtnl_tc *tc, int argc, char **argv)
|
|
+{
|
|
+ struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
|
|
+
|
|
+ for (;;) {
|
|
+ int c, optidx = 0;
|
|
+ enum {
|
|
+ ARG_DEFAULT = 257,
|
|
+ };
|
|
+ static struct option long_opts[] = {
|
|
+ { "help", 0, 0, 'h' },
|
|
+ { "default", 1, 0, ARG_DEFAULT },
|
|
+ { 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_hfsc_set_defcls(qdisc, nl_cli_parse_u32(optarg));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static void print_class_usage(void)
|
|
+{
|
|
+ printf(
|
|
+"Usage: nl-class-add [...] hfsc [OPTIONS]...\n"
|
|
+"\n"
|
|
+"OPTIONS\n"
|
|
+" Usage: ... hfsc [ [ --rt \"SC\" ] [ --ls \"SC\" ] | [--sc \"SC\" ] ] [--ul\"SC\" ]\n"
|
|
+" --help Show this help text.\n"
|
|
+" --realtime=\"SC\" Realtime service curve. Cannot be used with sc.\n"
|
|
+" --linkshare=\"SC\" Link sharing service curve. Cannot be used with ls\n"
|
|
+" --servicecurve=\"SC\" Combined service curve; Cannot be used with rt or ls.\n"
|
|
+" --upperlimit=\"SC\" Upper limit service curve.\n"
|
|
+" Slope Format\n"
|
|
+" SC := [ [ m1 BPS ] [ d SEC ] m2 BPS\n"
|
|
+" m1 : slope of first segment\n"
|
|
+" d : x-coordinate of intersection\n"
|
|
+" m2 : slope of second segment\n"
|
|
+" Alternative Format:\n"
|
|
+" SC := [ [ umax BYTE ] dmax SEC ] rate BPS\n"
|
|
+" umax : maximum unit of work\n"
|
|
+" dmax : maximum Delay\n"
|
|
+" rate : rate\n"
|
|
+"\n"
|
|
+" Note: Service curve definitions must be in quotes on the command line.\n"
|
|
+" Note: Must provide at least one service curve.\n"
|
|
+" Note: If Upper limit is provided, linkshare or combined service curve must be given.\n"
|
|
+"EXAMPLE"
|
|
+" # Attach class 1:1 to hfsc qdisc 1: and rate limit it to 20mbit\n"
|
|
+" nl-class-add --dev=eth1 --parent=1: --classid=1:1 hfsc --sc=\"rate 20mbit\"\n");
|
|
+}
|
|
+
|
|
+
|
|
+static int hfsc_get_sc_slope(char *optarg, struct service_curve *sc_out)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hfsc_get_sc_alt(char *optarg, struct service_curve *sc_out)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hfsc_get_sc(char *optarg, struct service_curve *sc_out)
|
|
+{
|
|
+ memset(sc_out, 0, sizeof(*sc_out));
|
|
+
|
|
+ if ((hfsc_get_sc_slope(optarg, sc) < 0) &&
|
|
+ (hfsc_get_sc_alt(optarg, sc) < 0))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if ((sc->m1 == 0) && (sc->m2 == 0)) {
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+static void hfsc_parse_class_argv(struct rtnl_tc *tc, int argc, char **argv)
|
|
+{
|
|
+ struct rtnl_class *class = (struct rtnl_class *) tc;
|
|
+ struct tc_service_curve sc;
|
|
+ long rate;
|
|
+
|
|
+ for (;;) {
|
|
+ int c, optidx = 0;
|
|
+ enum {
|
|
+ ARG_RT = 257,
|
|
+ ARG_LS = 258,
|
|
+ ARG_SC = 259,
|
|
+ ARG_UL = 260,
|
|
+ };
|
|
+ static struct option long_opts[] = {
|
|
+ { "help", 0, 0, 'h' },
|
|
+ { "realtime", 1, 0, ARG_RT },
|
|
+ { "linkshare", 1, 0, ARG_LS },
|
|
+ { "servicecurve", 1, 0, ARG_SC },
|
|
+ { "upperlimit", 1, 0, ARG_UL },
|
|
+ { 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:
|
|
+ rate = nl_size2int(optarg);
|
|
+ if (rate < 0) {
|
|
+ nl_cli_fatal(rate, "Unable to parse htb rate "
|
|
+ "\"%s\": Invalid format.", optarg);
|
|
+ }
|
|
+
|
|
+ rtnl_htb_set_rate(class, rate);
|
|
+ break;
|
|
+
|
|
+ case ARG_LS:
|
|
+ rate = nl_size2int(optarg);
|
|
+ if (rate < 0) {
|
|
+ nl_cli_fatal(rate, "Unable to parse htb ceil rate "
|
|
+ "\"%s\": Invalid format.", optarg);
|
|
+ }
|
|
+
|
|
+ rtnl_htb_set_ceil(class, rate);
|
|
+ break;
|
|
+
|
|
+ case ARG_SC:
|
|
+ rtnl_htb_set_prio(class, nl_cli_parse_u32(optarg));
|
|
+ break;
|
|
+
|
|
+ case ARG_UL:
|
|
+ rate = nl_size2int(optarg);
|
|
+ if (rate < 0) {
|
|
+ nl_cli_fatal(rate, "Unable to parse quantum "
|
|
+ "\"%s\": Invalid format.", optarg);
|
|
+ }
|
|
+
|
|
+ rtnl_htb_set_quantum(class, rate);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+static struct nl_cli_tc_module hfsc_qdisc_module =
|
|
+{
|
|
+ .tm_name = "hfsc",
|
|
+ .tm_type = RTNL_TC_TYPE_QDISC,
|
|
+ .tm_parse_argv = hfsc_parse_qdisc_argv,
|
|
+};
|
|
+
|
|
+#if 0
|
|
+static struct nl_cli_tc_module hfsc_class_module =
|
|
+{
|
|
+ .tm_name = "hfsc",
|
|
+ .tm_type = RTNL_TC_TYPE_CLASS,
|
|
+ .tm_parse_argv = hfsc_parse_class_argv,
|
|
+};
|
|
+#endif
|
|
+
|
|
+static void __init hfsc_init(void)
|
|
+{
|
|
+ nl_cli_tc_register(&hfsc_qdisc_module);
|
|
+// nl_cli_tc_register(&hfsc_class_module);
|
|
+}
|
|
+
|
|
+static void __exit hfsc_exit(void)
|
|
+{
|
|
+// nl_cli_tc_unregister(&hfsc_class_module);
|
|
+ nl_cli_tc_unregister(&hfsc_qdisc_module);
|
|
+}
|
|
diff --git a/lib/route/qdisc/hfsc.c b/lib/route/qdisc/hfsc.c
|
|
new file mode 100644
|
|
index 0000000..396c687
|
|
--- /dev/null
|
|
+++ b/lib/route/qdisc/hfsc.c
|
|
@@ -0,0 +1,404 @@
|
|
+/*
|
|
+ * lib/route/qdisc/hfsc.c HFSC Qdisc
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * @ingroup qdisc
|
|
+ * @ingroup class
|
|
+ * @defgroup qdisc_hfsc Hierachical Fair Service Curve (HFSC)
|
|
+ * @{
|
|
+ */
|
|
+
|
|
+#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/hfsc.h>
|
|
+
|
|
+#define USEC_PER_SEC 1000000
|
|
+
|
|
+/** @cond SKIP */
|
|
+#define SCH_HFSC_HAS_DEFCLS 0x02
|
|
+
|
|
+#define SCH_HFSC_HAS_RT_SC 0x001
|
|
+#define SCH_HFSC_HAS_FAIR_SC 0x002
|
|
+#define SCH_HFSC_HAS_UPPER_SC 0x004
|
|
+/** @endcond */
|
|
+
|
|
+static struct nla_policy hfsc_policy[TCA_HFSC_MAX + 1] = {
|
|
+ [TCA_HFSC_RSC] = { .minlen = sizeof(struct tc_service_curve) },
|
|
+ [TCA_HFSC_FSC] = { .minlen = sizeof(struct tc_service_curve) },
|
|
+ [TCA_HFSC_USC] = { .minlen = sizeof(struct tc_service_curve) },
|
|
+};
|
|
+
|
|
+static int hfsc_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
|
|
+{
|
|
+ struct rtnl_hfsc_qdisc *hfsc = data;
|
|
+ struct tc_hfsc_qopt *opt;
|
|
+
|
|
+ if (tc->tc_opts->d_size < sizeof(struct tc_hfsc_qopt))
|
|
+ return -NLE_INVAL;
|
|
+
|
|
+ opt = (struct tc_hfsc_qopt *) tc->tc_opts->d_data;
|
|
+ hfsc->qsc_defcls = opt->defcls;
|
|
+ hfsc->qsc_mask = SCH_HFSC_HAS_DEFCLS;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hfsc_class_msg_parser(struct rtnl_tc *tc, void *data)
|
|
+{
|
|
+ struct nlattr *tb[TCA_HFSC_MAX + 1];
|
|
+ struct rtnl_hfsc_class *hfsc = data;
|
|
+ int err;
|
|
+
|
|
+ if ((err = tca_parse(tb, TCA_HFSC_MAX, tc, hfsc_policy)) < 0)
|
|
+ return err;
|
|
+
|
|
+ hfsc->csc_mask = 0;
|
|
+ if (tb[TCA_HFSC_RSC]) {
|
|
+ nla_memcpy(&hfsc->csc_rt_sc, tb[TCA_HFSC_RSC],
|
|
+ sizeof(struct tc_service_curve));
|
|
+ hfsc->csc_mask |= SCH_HFSC_HAS_RT_SC;
|
|
+ }
|
|
+
|
|
+ if (tb[TCA_HFSC_FSC]) {
|
|
+ nla_memcpy(&hfsc->csc_fair_sc, tb[TCA_HFSC_FSC],
|
|
+ sizeof(struct tc_service_curve));
|
|
+ hfsc->csc_mask |= SCH_HFSC_HAS_FAIR_SC;
|
|
+ }
|
|
+
|
|
+ if (tb[TCA_HFSC_USC]) {
|
|
+ nla_memcpy(&hfsc->csc_upper_sc, tb[TCA_HFSC_USC],
|
|
+ sizeof(struct tc_service_curve));
|
|
+ hfsc->csc_mask |= SCH_HFSC_HAS_UPPER_SC;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void hfsc_qdisc_dump_line(struct rtnl_tc *tc, void *data,
|
|
+ struct nl_dump_params *p)
|
|
+{
|
|
+ struct rtnl_hfsc_qdisc *hfsc = data;
|
|
+
|
|
+ if (!hfsc)
|
|
+ return;
|
|
+
|
|
+ if (hfsc->qsc_mask & SCH_HFSC_HAS_DEFCLS) {
|
|
+ char buf[64];
|
|
+ nl_dump(p, " default-class %s",
|
|
+ rtnl_tc_handle2str(hfsc->qsc_defcls, buf, sizeof(buf)));
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static void hfsc_print_sc(struct nl_dump_params *p, char *name,
|
|
+ struct rtnl_curve *sc)
|
|
+{
|
|
+ double val;
|
|
+ char *unit;
|
|
+
|
|
+ nl_dump(p, "%s ", name);
|
|
+ val = nl_cancel_down_bits(sc->sc_m1, &unit);
|
|
+ nl_dump(p, " m1 %.2f%s/s", val, unit);
|
|
+
|
|
+ val = nl_cancel_down_us(sc->sc_d, &unit);
|
|
+ nl_dump(p, " d %.2f%s", val, unit);
|
|
+
|
|
+ val = nl_cancel_down_bits(sc->sc_m2, &unit);
|
|
+ nl_dump(p, " m2 %.2f%s/s", val, unit);
|
|
+}
|
|
+
|
|
+
|
|
+static void hfsc_class_dump_line(struct rtnl_tc *tc, void *data,
|
|
+ struct nl_dump_params *p)
|
|
+{
|
|
+ struct rtnl_hfsc_class *hfsc = data;
|
|
+
|
|
+ if (!hfsc)
|
|
+ return;
|
|
+
|
|
+ if ((hfsc->csc_mask & SCH_HFSC_HAS_RT_SC) &&
|
|
+ (hfsc->csc_mask & SCH_HFSC_HAS_FAIR_SC) &&
|
|
+ (memcmp(&hfsc->csc_rt_sc,
|
|
+ &hfsc->csc_fair_sc,
|
|
+ sizeof(hfsc->csc_rt_sc)) == 0)) {
|
|
+ hfsc_print_sc(p, "sc", &hfsc->csc_rt_sc);
|
|
+ } else {
|
|
+ if (hfsc->csc_mask & SCH_HFSC_HAS_RT_SC)
|
|
+ hfsc_print_sc(p, "rt", &hfsc->csc_rt_sc);
|
|
+ if (hfsc->csc_mask & SCH_HFSC_HAS_FAIR_SC)
|
|
+ hfsc_print_sc(p, "ls", &hfsc->csc_fair_sc);
|
|
+ }
|
|
+
|
|
+ if (hfsc->csc_mask & SCH_HFSC_HAS_UPPER_SC)
|
|
+ hfsc_print_sc(p, "ul", &hfsc->csc_upper_sc);
|
|
+}
|
|
+
|
|
+
|
|
+static int hfsc_qdisc_msg_fill(struct rtnl_tc *tc, void *data,
|
|
+ struct nl_msg *msg)
|
|
+{
|
|
+ struct rtnl_hfsc_qdisc *hfsc = data;
|
|
+ struct tc_hfsc_qopt opts = {0};
|
|
+
|
|
+ if (!hfsc || !(hfsc->qsc_mask & SCH_HFSC_HAS_DEFCLS))
|
|
+ return -NLE_INVAL;
|
|
+
|
|
+ opts.defcls = hfsc->qsc_defcls;
|
|
+
|
|
+ return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
|
|
+}
|
|
+
|
|
+/* FIXME: Verify we have at least one curve, and if upper, must have fair */
|
|
+static int hfsc_class_msg_fill(struct rtnl_tc *tc, void *data,
|
|
+ struct nl_msg *msg)
|
|
+{
|
|
+ struct rtnl_hfsc_class *hfsc = data;
|
|
+ struct tc_service_curve sc;
|
|
+
|
|
+ if (!hfsc)
|
|
+ BUG();
|
|
+
|
|
+ if (hfsc->csc_mask & SCH_HFSC_HAS_RT_SC) {
|
|
+ memcpy(&sc, &hfsc->csc_rt_sc, sizeof(sc));
|
|
+ } else {
|
|
+ memset(&sc, 0, sizeof(sc));
|
|
+ }
|
|
+ NLA_PUT(msg, TCA_HFSC_RSC, sizeof(sc), &sc);
|
|
+
|
|
+ if (hfsc->csc_mask & SCH_HFSC_HAS_FAIR_SC) {
|
|
+ memcpy(&sc, &hfsc->csc_fair_sc, sizeof(sc));
|
|
+ } else {
|
|
+ memset(&sc, 0, sizeof(sc));
|
|
+ }
|
|
+ NLA_PUT(msg, TCA_HFSC_FSC, sizeof(sc), &sc);
|
|
+
|
|
+ if (hfsc->csc_mask & SCH_HFSC_HAS_UPPER_SC) {
|
|
+ memcpy(&sc, &hfsc->csc_upper_sc, sizeof(sc));
|
|
+ } else {
|
|
+ memset(&sc, 0, sizeof(sc));
|
|
+ }
|
|
+ NLA_PUT(msg, TCA_HFSC_USC, sizeof(sc), &sc);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+nla_put_failure:
|
|
+ return -NLE_MSGSIZE;
|
|
+}
|
|
+
|
|
+static struct rtnl_tc_ops hfsc_qdisc_ops;
|
|
+static struct rtnl_tc_ops hfsc_class_ops;
|
|
+
|
|
+static struct rtnl_hfsc_qdisc *hfsc_qdisc_data(struct rtnl_qdisc *qdisc)
|
|
+{
|
|
+ return rtnl_tc_data_check(TC_CAST(qdisc), &hfsc_qdisc_ops);
|
|
+}
|
|
+
|
|
+static struct rtnl_hfsc_class *hfsc_class_data(struct rtnl_class *class)
|
|
+{
|
|
+ return rtnl_tc_data_check(TC_CAST(class), &hfsc_class_ops);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @name Attribute Modifications
|
|
+ * @{
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * Return default class of HFSC qdisc
|
|
+ * @arg qdisc hfsc qdisc object
|
|
+ *
|
|
+ * Returns the classid of the class where all unclassified traffic
|
|
+ * goes to.
|
|
+ *
|
|
+ * @return classid or TC_H_UNSPEC if unspecified.
|
|
+ */
|
|
+uint32_t rtnl_hfsc_get_defcls(struct rtnl_qdisc *qdisc)
|
|
+{
|
|
+ struct rtnl_hfsc_qdisc *hfsc;
|
|
+
|
|
+ if ((hfsc = hfsc_qdisc_data(qdisc)) &&
|
|
+ hfsc->qsc_mask & SCH_HFSC_HAS_DEFCLS)
|
|
+ return hfsc->qsc_defcls;
|
|
+
|
|
+ return TC_H_UNSPEC;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Set default class of the hfsc qdisc to the specified value
|
|
+ * @arg qdisc qdisc to change
|
|
+ * @arg defcls new default class
|
|
+ */
|
|
+int rtnl_hfsc_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
|
|
+{
|
|
+ struct rtnl_hfsc_qdisc *hfsc;
|
|
+
|
|
+ if (!(hfsc = hfsc_qdisc_data(qdisc)))
|
|
+ return -NLE_OPNOTSUPP;
|
|
+
|
|
+ hfsc->qsc_defcls = defcls;
|
|
+ hfsc->qsc_mask |= SCH_HFSC_HAS_DEFCLS;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* m1-d-m2: Direct Method of Specifying service curve slopes */
|
|
+void rtnl_hfsc_get_realtime_sc(struct rtnl_class *cls,
|
|
+ struct tc_service_curve *curve_out)
|
|
+{
|
|
+ struct rtnl_hfsc_class *hfsc;
|
|
+
|
|
+ if ((hfsc = hfsc_class_data(cls)) &&
|
|
+ (hfsc->csc_mask & SCH_HFSC_HAS_RT_SC)) {
|
|
+ memcpy(curve_out, &hfsc->csc_rt_sc, sizeof(*curve_out));
|
|
+ } else {
|
|
+ memset(curve_out, 0, sizeof(*curve_out));
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+int rtnl_hfsc_set_realtime_sc(struct rtnl_class *cls,
|
|
+ struct tc_service_curve *curve_in)
|
|
+{
|
|
+ struct rtnl_hfsc_class *hfsc;
|
|
+
|
|
+ if (!(hfsc = hfsc_class_data(cls)))
|
|
+ return -NLE_OPNOTSUPP;
|
|
+
|
|
+ memcpy(&hfsc->csc_rt_sc, curve_in, sizeof(hfsc->csc_rt_sc));
|
|
+ hfsc->csc_mask |= SCH_HFSC_HAS_RT_SC;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void rtnl_hfsc_get_fair_sc(struct rtnl_class *cls,
|
|
+ struct tc_service_curve *curve_out)
|
|
+{
|
|
+ struct rtnl_hfsc_class *hfsc;
|
|
+
|
|
+ if ((hfsc = hfsc_class_data(cls)) &&
|
|
+ (hfsc->csc_mask & SCH_HFSC_HAS_FAIR_SC)) {
|
|
+ memcpy(curve_out, &hfsc->csc_fair_sc, sizeof(*curve_out));
|
|
+ } else {
|
|
+ memset(curve_out, 0, sizeof(*curve_out));
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+int rtnl_hfsc_set_fair_sc(struct rtnl_class *cls,
|
|
+ struct tc_service_curve *curve_in)
|
|
+{
|
|
+ struct rtnl_hfsc_class *hfsc;
|
|
+
|
|
+ if (!(hfsc = hfsc_class_data(cls)))
|
|
+ return -NLE_OPNOTSUPP;
|
|
+
|
|
+ memcpy(&hfsc->csc_fair_sc, curve_in, sizeof(hfsc->csc_fair_sc));
|
|
+ hfsc->csc_mask |= SCH_HFSC_HAS_FAIR_SC;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void rtnl_hfsc_get_upperlimit_sc(struct rtnl_class *cls,
|
|
+ struct tc_service_curve *curve_out)
|
|
+{
|
|
+ struct rtnl_hfsc_class *hfsc;
|
|
+
|
|
+ if ((hfsc = hfsc_class_data(cls)) &&
|
|
+ (hfsc->csc_mask & SCH_HFSC_HAS_UPPER_SC)) {
|
|
+ memcpy(curve_out, &hfsc->csc_upper_sc, sizeof(*curve_out));
|
|
+ } else {
|
|
+ memset(curve_out, 0, sizeof(*curve_out));
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+int rtnl_hfsc_set_upperlimit_sc(struct rtnl_class *cls,
|
|
+ struct tc_service_curve *curve_in)
|
|
+{
|
|
+ struct rtnl_hfsc_class *hfsc;
|
|
+
|
|
+ if (!(hfsc = hfsc_class_data(cls)))
|
|
+ return -NLE_OPNOTSUPP;
|
|
+
|
|
+ memcpy(&hfsc->csc_upper_sc, curve_in, sizeof(hfsc->csc_upper_sc));
|
|
+ hfsc->csc_mask |= SCH_HFSC_HAS_UPPER_SC;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* umax-dmax-rate Method of specifying service curve translations */
|
|
+int rtnl_hfsc_spec_to_sc(struct hfsc_spec *spec_in,
|
|
+ struct tc_service_curve *curve_out)
|
|
+{
|
|
+ unsigned int umax, dmax, rate;
|
|
+
|
|
+ umax = spec_in->work_max;
|
|
+ dmax = spec_in->delay_max;
|
|
+ rate = spec_in->rate;
|
|
+
|
|
+ if (umax != 0 && dmax == 0) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (rate == 0) {
|
|
+ curve_out->m1 = 0;
|
|
+ curve_out->d = 0;
|
|
+ curve_out->m2 = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* concave: slope of first segment is umax/dmax, intersect at dmax */
|
|
+ if ((dmax != 0) && (ceil(1.0 * umax * USEC_PER_SEC / dmax) > rate)) {
|
|
+ curve_out->m1 = ceil(1.0 * umax * USEC_PER_SEC / dmax);
|
|
+ curve_out->d = dmax;
|
|
+ curve_out->m2 = rate;
|
|
+ } else {
|
|
+ /* convex: slope of first segment = 0, intersect (dmax - umax / rate */
|
|
+ /* FIXME: is that calc correct? Maybe it's (dmax - umax) / rate? */
|
|
+ curve_out->m1 = 0;
|
|
+ curve_out->d = ceil(dmax - umax * USEC_PER_SEC / rate);
|
|
+ curve_out->m2 = rate;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/** @} */
|
|
+
|
|
+static struct rtnl_tc_ops hfsc_qdisc_ops = {
|
|
+ .to_kind = "hfsc",
|
|
+ .to_type = RTNL_TC_TYPE_QDISC,
|
|
+ .to_size = sizeof(struct rtnl_hfsc_qdisc),
|
|
+ .to_msg_parser = hfsc_qdisc_msg_parser,
|
|
+ .to_dump[NL_DUMP_LINE] = hfsc_qdisc_dump_line,
|
|
+ .to_msg_fill = hfsc_qdisc_msg_fill,
|
|
+};
|
|
+
|
|
+static struct rtnl_tc_ops hfsc_class_ops = {
|
|
+ .to_kind = "hfsc",
|
|
+ .to_type = RTNL_TC_TYPE_CLASS,
|
|
+ .to_size = sizeof(struct rtnl_hfsc_class),
|
|
+ .to_msg_parser = hfsc_class_msg_parser,
|
|
+ .to_dump[NL_DUMP_LINE] = hfsc_class_dump_line,
|
|
+ .to_msg_fill = hfsc_class_msg_fill,
|
|
+};
|
|
+
|
|
+static void __init hfsc_init(void)
|
|
+{
|
|
+ rtnl_tc_register(&hfsc_qdisc_ops);
|
|
+ rtnl_tc_register(&hfsc_class_ops);
|
|
+}
|
|
+
|
|
+static void __exit hfsc_exit(void)
|
|
+{
|
|
+ rtnl_tc_unregister(&hfsc_qdisc_ops);
|
|
+ rtnl_tc_unregister(&hfsc_class_ops);
|
|
+}
|