diff options
author | Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se> | 2018-02-23 14:37:07 (GMT) |
---|---|---|
committer | Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se> | 2018-07-26 09:17:37 (GMT) |
commit | 25cf1d39edede76efcb2554bfb82afa429d762a7 (patch) | |
tree | 692ddf602a7b7294e91aea526a572de671d4bd7c | |
parent | 0fc332dc9c681a6dc5dc1a8b4ff31fc40ba1c8d0 (diff) | |
download | libnl-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.am | 2 | ||||
-rw-r--r-- | include/netlink-private/types.h | 14 | ||||
-rw-r--r-- | include/netlink/route/qdisc/mqprio.h | 48 | ||||
-rw-r--r-- | lib/route/qdisc/mqprio.c | 604 | ||||
-rw-r--r-- | libnl-route-3.sym | 16 |
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; |