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

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);
+}