summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolodymyr Bendiuga <volodymyr.bendiuga@westermo.se>2018-02-23 14:37:07 (GMT)
committerVolodymyr Bendiuga <volodymyr.bendiuga@westermo.se>2018-07-26 09:17:37 (GMT)
commit25cf1d39edede76efcb2554bfb82afa429d762a7 (patch)
tree692ddf602a7b7294e91aea526a572de671d4bd7c
parent0fc332dc9c681a6dc5dc1a8b4ff31fc40ba1c8d0 (diff)
downloadlibnl-25cf1d39edede76efcb2554bfb82afa429d762a7.zip
libnl-25cf1d39edede76efcb2554bfb82afa429d762a7.tar.gz
libnl-25cf1d39edede76efcb2554bfb82afa429d762a7.tar.bz2
route:qdisc: add MQPRIO Qdisc
More about Qdisc mqprio can be found at: http://man7.org/linux/man-pages/man8/tc-mqprio.8.html Signed-off-by: Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se>
-rw-r--r--Makefile.am2
-rw-r--r--include/netlink-private/types.h14
-rw-r--r--include/netlink/route/qdisc/mqprio.h48
-rw-r--r--lib/route/qdisc/mqprio.c604
-rw-r--r--libnl-route-3.sym16
5 files changed, 684 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 8e7180e..82d68a5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -165,6 +165,7 @@ libnlinclude_netlink_route_qdisc_HEADERS = \
include/netlink/route/qdisc/netem.h \
include/netlink/route/qdisc/plug.h \
include/netlink/route/qdisc/prio.h \
+ include/netlink/route/qdisc/mqprio.h \
include/netlink/route/qdisc/red.h \
include/netlink/route/qdisc/sfq.h \
include/netlink/route/qdisc/tbf.h
@@ -409,6 +410,7 @@ lib_libnl_route_3_la_SOURCES = \
lib/route/qdisc/netem.c \
lib/route/qdisc/plug.c \
lib/route/qdisc/prio.c \
+ lib/route/qdisc/mqprio.c \
lib/route/qdisc/red.c \
lib/route/qdisc/sfq.c \
lib/route/qdisc/tbf.c \
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index de93bd9..0c6e2ea 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -679,6 +679,20 @@ struct rtnl_prio
uint32_t qp_mask;
};
+struct rtnl_mqprio
+{
+ uint8_t qm_num_tc;
+ uint8_t qm_prio_map[TC_QOPT_BITMASK + 1];
+ uint8_t qm_hw;
+ uint16_t qm_count[TC_QOPT_MAX_QUEUE];
+ uint16_t qm_offset[TC_QOPT_MAX_QUEUE];
+ uint16_t qm_mode;
+ uint16_t qm_shaper;
+ uint64_t qm_min_rate[TC_QOPT_MAX_QUEUE];
+ uint64_t qm_max_rate[TC_QOPT_MAX_QUEUE];
+ uint32_t qm_mask;
+};
+
struct rtnl_tbf
{
uint32_t qt_limit;
diff --git a/include/netlink/route/qdisc/mqprio.h b/include/netlink/route/qdisc/mqprio.h
new file mode 100644
index 0000000..5c7644a
--- /dev/null
+++ b/include/netlink/route/qdisc/mqprio.h
@@ -0,0 +1,48 @@
+/*
+ * lib/route/qdisc/mqprio.c MQPRIO Qdisc/Class
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2018 Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se>
+ */
+
+#ifndef NETLINK_MQPRIO_H_
+#define NETLINK_MQPRIO_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc);
+extern int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
+ int len);
+extern uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload);
+extern int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[],
+ uint16_t offset[], int len);
+extern int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count,
+ uint16_t *offset);
+extern int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode);
+extern int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper);
+extern int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[],
+ int len);
+extern int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min);
+extern int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[],
+ int len);
+extern int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETLINK_MQPRIO_H_ */
diff --git a/lib/route/qdisc/mqprio.c b/lib/route/qdisc/mqprio.c
new file mode 100644
index 0000000..c9f0297
--- /dev/null
+++ b/lib/route/qdisc/mqprio.c
@@ -0,0 +1,604 @@
+/*
+ * lib/route/qdisc/mqprio.c MQPRIO Qdisc/Class
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2018 Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/qdisc/mqprio.h>
+
+/** @cond SKIP */
+#define SCH_MQPRIO_ATTR_NUMTC 1<<0
+#define SCH_MQPRIO_ATTR_PRIOMAP 1<<1
+#define SCH_MQPRIO_ATTR_HW 1<<2
+#define SCH_MQPRIO_ATTR_QUEUE 1<<3
+#define SCH_MQPRIO_ATTR_MODE 1<<4
+#define SCH_MQPRIO_ATTR_SHAPER 1<<5
+#define SCH_MQPRIO_ATTR_MIN_RATE 1<<6
+#define SCH_MQPRIO_ATTR_MAX_RATE 1<<7
+/** @endcond */
+
+static struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = {
+ [TCA_MQPRIO_MODE] = { .minlen = sizeof(uint16_t) },
+ [TCA_MQPRIO_SHAPER] = { .minlen = sizeof(uint16_t) },
+ [TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED },
+ [TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED },
+};
+
+static int mqprio_msg_parser(struct rtnl_tc *tc, void *data)
+{
+ struct rtnl_mqprio *mqprio = data;
+ struct tc_mqprio_qopt *qopt;
+ struct nlattr *attr;
+ int len, rem, i, err;
+
+ if (tc->tc_opts->d_size < sizeof(*qopt))
+ return -NLE_INVAL;
+
+ qopt = (struct tc_mqprio_qopt *) tc->tc_opts->d_data;
+ mqprio->qm_num_tc = qopt->num_tc;
+ mqprio->qm_hw = qopt->hw;
+ memcpy(mqprio->qm_prio_map, qopt->prio_tc_map,
+ TC_QOPT_MAX_QUEUE * sizeof(uint8_t));
+ memcpy(mqprio->qm_count, qopt->count,
+ TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+ memcpy(mqprio->qm_offset, qopt->offset,
+ TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+ mqprio->qm_mask = (SCH_MQPRIO_ATTR_NUMTC | SCH_MQPRIO_ATTR_PRIOMAP |
+ SCH_MQPRIO_ATTR_QUEUE | SCH_MQPRIO_ATTR_HW);
+
+ len = tc->tc_opts->d_size - NLA_ALIGN(sizeof(*qopt));
+
+ if (len > 0) {
+ struct nlattr *tb[TCA_MQPRIO_MAX + 1];
+
+ err = nla_parse(tb, TCA_MQPRIO_MAX, (struct nlattr *)
+ (tc->tc_opts->d_data + NLA_ALIGN(sizeof(*qopt))),
+ len, mqprio_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[TCA_MQPRIO_MODE]) {
+ mqprio->qm_mode = nla_get_u16(tb[TCA_MQPRIO_MODE]);
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE;
+ }
+
+ if (tb[TCA_MQPRIO_SHAPER]) {
+ mqprio->qm_shaper = nla_get_u16(tb[TCA_MQPRIO_SHAPER]);
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER;
+ }
+
+ if (tb[TCA_MQPRIO_MIN_RATE64]) {
+ i = 0;
+ nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64], rem) {
+ if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64)
+ return -EINVAL;
+
+ if (i >= mqprio->qm_num_tc)
+ break;
+
+ mqprio->qm_min_rate[i] = nla_get_u64(attr);
+ }
+
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE;
+ }
+
+ if (tb[TCA_MQPRIO_MAX_RATE64]) {
+ i = 0;
+ nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64], rem) {
+ if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64)
+ return -EINVAL;
+
+ if (i >= mqprio->qm_num_tc)
+ break;
+
+ mqprio->qm_max_rate[i] = nla_get_u64(attr);
+ }
+
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE;
+ }
+ }
+
+ return 0;
+}
+
+static int mqprio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+ struct rtnl_mqprio *mqprio = data;
+ struct tc_mqprio_qopt qopt = { 0 };
+ struct nlattr *nest = NULL;
+ int i;
+
+ if (!mqprio ||
+ !(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC) ||
+ !(mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP) ||
+ !(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE))
+ BUG();
+
+ if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
+ qopt.hw = 0;
+ else
+ qopt.hw = mqprio->qm_hw;
+
+ qopt.num_tc = mqprio->qm_num_tc;
+ memcpy(qopt.count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+ memcpy(qopt.offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+ memcpy(qopt.prio_tc_map, mqprio->qm_prio_map, TC_QOPT_MAX_QUEUE * sizeof(uint8_t));
+
+ nlmsg_append(msg, &qopt, sizeof(qopt), NL_DONTPAD);
+
+ if (mqprio->qm_hw) {
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE)
+ NLA_PUT_U16(msg, TCA_MQPRIO_MODE, mqprio->qm_mode);
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)
+ NLA_PUT_U16(msg, TCA_MQPRIO_SHAPER, mqprio->qm_shaper);
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) {
+ nest = nla_nest_start(msg, TCA_MQPRIO_MIN_RATE64);
+ if (!nest)
+ goto nla_put_failure;
+
+ for (i = 0; i < mqprio->qm_num_tc; i++) {
+ if (nla_put(msg, TCA_MQPRIO_MIN_RATE64,
+ sizeof(mqprio->qm_min_rate[i]),
+ &mqprio->qm_min_rate[i]) < 0)
+ goto nla_nest_cancel;
+ }
+ nla_nest_end(msg, nest);
+ }
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) {
+ nest = nla_nest_start(msg, TCA_MQPRIO_MAX_RATE64);
+ if (!nest)
+ goto nla_put_failure;
+
+ for (i = 0; i < mqprio->qm_num_tc; i++) {
+ if (nla_put(msg, TCA_MQPRIO_MAX_RATE64,
+ sizeof(mqprio->qm_max_rate[i]),
+ &mqprio->qm_max_rate[i]) < 0)
+ goto nla_nest_cancel;
+ }
+ nla_nest_end(msg, nest);
+ }
+ }
+
+ return 0;
+
+ nla_nest_cancel:
+ nla_nest_cancel(msg, nest);
+ return -NLE_MSGSIZE;
+
+ nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static void mqprio_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_mqprio *mqprio = data;
+
+ if (mqprio)
+ nl_dump(p, " num_tc %u", mqprio->qm_num_tc);
+}
+
+static void mqprio_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_mqprio *mqprio = data;
+ int i;
+
+ if (!mqprio)
+ return;
+
+ nl_dump(p, "map [");
+
+ for (i = 0; i <= TC_QOPT_BITMASK; i++)
+ nl_dump(p, "%u%s", mqprio->qm_prio_map[i],
+ i < TC_QOPT_BITMASK ? " " : "");
+
+ nl_dump(p, "]\n");
+ nl_new_line(p);
+}
+
+/**
+ * @name Attribute Modification
+ * @{
+ */
+
+/**
+ * Set number of traffic classes.
+ * @arg qdisc MQPRIO qdisc to be modified.
+ * @arg num_tc Number of traffic classes to create.
+ * @return 0 on success or a negative error code.
+ */
+void rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ mqprio->qm_num_tc = num_tc;
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_NUMTC;
+}
+
+/**
+ * Get number of traffic classes of MQPRIO qdisc.
+ * @arg qdisc MQPRIO qdisc.
+ * @return Number of traffic classes or a negative error code.
+ */
+int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+ BUG();
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC)
+ return mqprio->qm_num_tc;
+ else
+ return -NLE_MISSING_ATTR;
+}
+
+/**
+ * Set priomap of the MQPRIO qdisc.
+ * @arg qdisc MQPRIO qdisc to be modified.
+ * @arg priomap New priority mapping.
+ * @arg len Length of priomap (# of elements).
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
+ int len)
+{
+ struct rtnl_mqprio *mqprio;
+ int i;
+
+ if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC))
+ return -NLE_MISSING_ATTR;
+
+ if ((len / sizeof(uint8_t)) > (TC_QOPT_BITMASK+1))
+ return -NLE_RANGE;
+
+ for (i = 0; i <= TC_QOPT_BITMASK; i++) {
+ if (priomap[i] > mqprio->qm_num_tc)
+ return -NLE_RANGE;
+ }
+
+ memcpy(mqprio->qm_prio_map, priomap, len * sizeof(uint8_t));
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_PRIOMAP;
+
+ return 0;
+}
+
+/**
+ * Get priomap of MQPRIO qdisc.
+ * @arg qdisc MQPRIO qdisc.
+ * @return Priority mapping as array of size TC_QOPT_BANDS+1
+ * or NULL if an error occured.
+ */
+uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+ BUG();
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP)
+ return mqprio->qm_prio_map;
+ else
+ return NULL;
+}
+
+/**
+ * Offload to HW or run in SW (default).
+ * @arg qdisc MQPRIO qdisc to be modified.
+ * @arg offload 1 - offload to HW, 0 - run in SW only (default).
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ switch (offload) {
+ case 0:
+ case 1:
+ mqprio->qm_hw = offload;
+ break;
+ default:
+ return -NLE_INVAL;
+ }
+
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_HW;
+ return 0;
+}
+
+/**
+ * Check whether running in HW or SW.
+ * @arg qdisc MQPRIO qdisc to be modified.
+ * @return 0 if running in SW, otherwise 1 (HW)
+ */
+int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+ BUG();
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)
+ return mqprio->qm_hw;
+
+ return 0;
+}
+
+/**
+ * Set tc queue of the MQPRIO qdisc.
+ * @arg qdisc MQPRIO qdisc to be modified.
+ * @arg count count of queue range for each traffic class
+ * @arg offset offset of queue range for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[],
+ uint16_t offset[], int len)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC))
+ return -NLE_MISSING_ATTR;
+
+ if ((len / sizeof(uint16_t)) > TC_QOPT_MAX_QUEUE)
+ return -NLE_RANGE;
+
+ memcpy(mqprio->qm_count, count, len * sizeof(uint16_t));
+ memcpy(mqprio->qm_offset, offset, len * sizeof(uint16_t));
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_QUEUE;
+
+ return 0;
+}
+
+/**
+ * Get tc queue of the MQPRIO qdisc.
+ * @arg qdisc MQPRIO qdisc to be modified.
+ * @arg count count of queue range for each traffic class
+ * @arg offset offset of queue range for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count,
+ uint16_t *offset)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+ BUG();
+
+ if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE))
+ return -NLE_MISSING_ATTR;
+
+ memcpy(count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+ memcpy(offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+
+ return 0;
+}
+
+/**
+ * Set mode of mqprio Qdisc
+ * @arg qdisc MQPRIO qdisc to be modified.
+ * @arg mode one of: TC_MQPRIO_MODE_DCB, TC_MQPRIO_MODE_CHANNEL
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
+ return -NLE_MISSING_ATTR;
+
+ mqprio->qm_mode = mode;
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE;
+
+ return 0;
+}
+
+/**
+ * Get mode of mqprio Qdisc
+ * @arg qdisc MQPRIO qdisc.
+ * @return mode on success or negative error code.
+ */
+int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+ BUG();
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE)
+ return mqprio->qm_mode;
+ else
+ return -NLE_MISSING_ATTR;
+}
+
+/**
+ * Set shaper of mqprio Qdisc
+ * @arg qdisc MQPRIO qdisc to be modified.
+ * @arg shaper one of: TC_MQPRIO_SHAPER_DCB, TC_MQPRIO_SHAPER_BW_RATE
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
+ return -NLE_MISSING_ATTR;
+
+ mqprio->qm_shaper = shaper;
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER;
+
+ return 0;
+}
+
+/**
+ * Get shaper of mqprio Qdisc
+ * @arg qdisc MQPRIO qdisc.
+ * @return shaper on success or negative error code.
+ */
+int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+ BUG();
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)
+ return mqprio->qm_shaper;
+ else
+ return -NLE_MISSING_ATTR;
+}
+
+/**
+ * Set minimum value of bandwidth rate limit for each traffic class
+ * @arg qdisc MQPRIO qdisc.
+ * @arg min minimum rate for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[], int len)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER))
+ return -NLE_MISSING_ATTR;
+
+ if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE)
+ return -NLE_INVAL;
+
+ if ((len / sizeof(uint64_t)) > TC_QOPT_MAX_QUEUE)
+ return -NLE_RANGE;
+
+ memcpy(mqprio->qm_min_rate, min, len * sizeof(uint64_t));
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE;
+
+ return 0;
+}
+
+/**
+ * Get minimum value of bandwidth rate limit for each traffic class
+ * @arg qdisc MQPRIO qdisc.
+ * @arg min minimum rate for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+ BUG();
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) {
+ memcpy(min, mqprio->qm_min_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t));
+ return 0;
+ }
+
+ return -NLE_MISSING_ATTR;
+}
+
+/**
+ * Set maximum value of bandwidth rate limit for each traffic class
+ * @arg qdisc MQPRIO qdisc.
+ * @arg max maximum rate for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[], int len)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER))
+ return -NLE_MISSING_ATTR;
+
+ if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE)
+ return -NLE_INVAL;
+
+ if ((len / sizeof(uint64_t)) > TC_QOPT_MAX_QUEUE)
+ return -NLE_RANGE;
+
+ memcpy(mqprio->qm_max_rate, max, len * sizeof(uint64_t));
+ mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE;
+
+ return 0;
+}
+
+/**
+ * Get maximum value of bandwidth rate limit for each traffic class
+ * @arg qdisc MQPRIO qdisc.
+ * @arg min maximum rate for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max)
+{
+ struct rtnl_mqprio *mqprio;
+
+ if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+ BUG();
+
+ if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) {
+ memcpy(max, mqprio->qm_max_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t));
+ return 0;
+ }
+
+ return -NLE_MISSING_ATTR;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops mqprio_ops = {
+ .to_kind = "mqprio",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_mqprio),
+ .to_msg_parser = mqprio_msg_parser,
+ .to_dump = {
+ [NL_DUMP_LINE] = mqprio_dump_line,
+ [NL_DUMP_DETAILS] = mqprio_dump_details,
+ },
+ .to_msg_fill = mqprio_msg_fill,
+};
+
+static void __init mqprio_init(void)
+{
+ rtnl_tc_register(&mqprio_ops);
+}
+
+static void __exit mqprio_exit(void)
+{
+ rtnl_tc_unregister(&mqprio_ops);
+}
+
+/** @} */
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index c76cbf8..a724d86 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -1109,4 +1109,20 @@ global:
rtnl_neigh_get_by_vlan;
rtnl_neigh_get_master;
rtnl_neigh_set_master;
+ rtnl_qdisc_mqprio_set_num_tc;
+ rtnl_qdisc_mqprio_get_num_tc;
+ rtnl_qdisc_mqprio_set_priomap;
+ rtnl_qdisc_mqprio_get_priomap;
+ rtnl_qdisc_mqprio_hw_offload;
+ rtnl_qdisc_mqprio_get_hw_offload;
+ rtnl_qdisc_mqprio_set_queue;
+ rtnl_qdisc_mqprio_get_queue;
+ rtnl_qdisc_mqprio_set_mode;
+ rtnl_qdisc_mqprio_get_mode;
+ rtnl_qdisc_mqprio_set_shaper;
+ rtnl_qdisc_mqprio_get_shaper;
+ rtnl_qdisc_mqprio_set_min_rate;
+ rtnl_qdisc_mqprio_get_min_rate;
+ rtnl_qdisc_mqprio_set_max_rate;
+ rtnl_qdisc_mqprio_get_max_rate;
} libnl_3_4;