From 9633a632831f420b56327003ba06dc9cb6bd42b7 Mon Sep 17 00:00:00 2001 From: Krzysztof Bembnista Date: Thu, 19 Jul 2018 23:41:40 +0200 Subject: [08/17] mqprio: implemented mqprio qdisc support --- Makefile.am | 2 + include/netlink-private/types.h | 10 + include/netlink/route/qdisc/mqprio.h | 65 +++++ lib/route/qdisc/mqprio.c | 393 +++++++++++++++++++++++++++ libnl-route-3.sym | 10 + 5 files changed, 480 insertions(+) create mode 100644 include/netlink/route/qdisc/mqprio.h create mode 100644 lib/route/qdisc/mqprio.c diff --git a/Makefile.am b/Makefile.am index efebf31..1a1b27c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -166,6 +166,7 @@ libnlinclude_netlink_route_qdisc_HEADERS = \ include/netlink/route/qdisc/hfsc.h \ include/netlink/route/qdisc/htb.h \ include/netlink/route/qdisc/netem.h \ + include/netlink/route/qdisc/mqprio.h \ include/netlink/route/qdisc/plug.h \ include/netlink/route/qdisc/prio.h \ include/netlink/route/qdisc/red.h \ @@ -410,6 +411,7 @@ lib_libnl_route_3_la_SOURCES = \ lib/route/qdisc/fifo.c \ lib/route/qdisc/fq_codel.c \ lib/route/qdisc/hfsc.c \ + lib/route/qdisc/mqprio.c \ lib/route/qdisc/htb.c \ lib/route/qdisc/ingress.c \ lib/route/qdisc/clsact.c \ diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h index 1672e3e..68f726c 100644 --- a/include/netlink-private/types.h +++ b/include/netlink-private/types.h @@ -685,6 +685,16 @@ struct rtnl_prio uint32_t qp_mask; }; +struct rtnl_mqprio +{ + uint8_t qmp_num_tc; + uint8_t qmp_prio_tc_map[TC_QOPT_BITMASK + 1]; + uint8_t qmp_hw; + uint16_t qmp_count[TC_QOPT_MAX_QUEUE]; + uint16_t qmp_offset[TC_QOPT_MAX_QUEUE]; + uint32_t qmp_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..94f4c30 --- /dev/null +++ b/include/netlink/route/qdisc/mqprio.h @@ -0,0 +1,65 @@ +/* + * lib/route/qdisc/mqprio.c mqprio + * + * 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 Intel Corporation + */ + +#ifndef NETLINK_MQPRIO_H_ +#define NETLINK_MQPRIO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Default Values + * @{ + */ + +/** + * Default number of traffic classes. + * @ingroup mqprio + */ +#define QDISC_MQPRIO_DEFAULT_NUM_TC 8 + +/** + * Default priority mapping. + * @ingroup mqprio + */ +#define QDISC_MQPRIO_DEFAULT_PRIO_MAP \ + {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 1, 3, 3, 3, 3} + +/** + * Use hardware QoS by default, use 0 to override defaults with user values. + * @ingroup mqprio + */ +#define QDISC_MQPRIO_DEFAULT_HW 1 + +/** @} */ + +extern void rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *); +extern int rtnl_qdisc_mqprio_set_prio_map(struct rtnl_qdisc *, + const uint8_t[], int); +extern uint8_t *rtnl_qdisc_mqprio_get_prio_map(struct rtnl_qdisc *); +extern void rtnl_qdisc_mqprio_set_hw(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_mqprio_get_hw(struct rtnl_qdisc *); +extern int rtnl_qdisc_mqprio_set_count(struct rtnl_qdisc *, + const uint16_t[], int); +extern uint16_t *rtnl_qdisc_mqprio_get_count(struct rtnl_qdisc *); +extern int rtnl_qdisc_mqprio_set_offset(struct rtnl_qdisc *, + const uint16_t[], int); +extern uint16_t *rtnl_qdisc_mqprio_get_offset(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/route/qdisc/mqprio.c b/lib/route/qdisc/mqprio.c new file mode 100644 index 0000000..5becb76 --- /dev/null +++ b/lib/route/qdisc/mqprio.c @@ -0,0 +1,393 @@ +/* + * lib/route/qdisc/mqprio.c mqprio + * + * 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 Intel Corporation + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_mqprio Multiqueue Prio + * @brief + * + * @par 1) Typical MQPRIO configuration + * @code + * // Specify the maximal number of traffix queues (TCs) + * rtnl_qdisc_mqprio_set_num_tc(qdisc, QDISC_MQPRIO_DEFAULT_NUM_TC); + * + * // Specify if hardware QoS is used + * rtnl_qdisc_mqprio_set_hw(qdisc, QDISC_MQPRIO_DEFAULT_HW); + * + * // Provide a map assigning each priority to a band number. + * uint8_t map[] = QDISC_MQPRIO_DEFAULT_PRIO_MAP; + * rtnl_qdisc_mqprio_set_prio_map(qdisc, map, sizeof(map)); + * + * // Provide counter@offset values. For example, values in tc + * // 'queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7', do this: + * uint16_t counter[] = {1, 1, 1, 1, 1, 1, 1, 1}; + * uint16_t offset[] = {0, 1, 2, 3, 4, 5, 6, 7}; + * rtnl_qdisc_mqprio_set_count(qdisc, counter, + * sizeof(counter)/sizeof(uint16_t)); + * rtnl_qdisc_mqprio_set_offset(qdisc, offset, sizeof(offset)/sizeof(uint16_t)); + * + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_MQPRIO_ATTR_NUM_TC 1 +#define SCH_MQPRIO_ATTR_PRIOMAP 2 +#define SCH_MQPRIO_ATTR_HW 4 +#define SCH_MQPRIO_ATTR_COUNTER 8 +#define SCH_MQPRIO_ATTR_OFFSET 16 +/** @endcond */ + +static int mqprio_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mqprio *mqprio = data; + struct tc_mqprio_qopt *opt; + + if (tc->tc_opts->d_size < sizeof(*opt)) + return -NLE_INVAL; + + opt = (struct tc_mqprio_qopt *) tc->tc_opts->d_data; + + mqprio->qmp_num_tc = opt->num_tc; + mqprio->qmp_hw = opt->hw; + + memcpy(mqprio->qmp_prio_tc_map, opt->prio_tc_map, + sizeof(mqprio->qmp_prio_tc_map)); + + memcpy(mqprio->qmp_count, opt->count, sizeof(mqprio->qmp_count)); + + memcpy(mqprio->qmp_offset, opt->offset, sizeof(mqprio->qmp_offset)); + + mqprio->qmp_mask = (SCH_MQPRIO_ATTR_NUM_TC | SCH_MQPRIO_ATTR_PRIOMAP | + SCH_MQPRIO_ATTR_HW | SCH_MQPRIO_ATTR_COUNTER | + SCH_MQPRIO_ATTR_OFFSET); + + return 0; +} + +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 hw %u", mqprio->qmp_num_tc, + mqprio->qmp_hw); +} + +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_PRIO_MAX; i++) + nl_dump(p, "%u", mqprio->qmp_prio_tc_map[i]); + + nl_dump(p, "\n"); + + nl_new_line(p); + + nl_dump(p, "queue"); + for (i = 0; i <= TC_PRIO_MAX && i <= mqprio->qmp_num_tc; i++) + nl_dump(p, " %u@%u", mqprio->qmp_count[i], + mqprio->qmp_offset[i]); + + nl_dump(p, "\n"); +} + +static int mqprio_msg_fill_raw(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_mqprio *mqprio = data; + struct tc_mqprio_qopt opts; + + if (!mqprio) + BUG(); + + if (!((mqprio->qmp_mask & SCH_MQPRIO_ATTR_OFFSET) && + (mqprio->qmp_mask & SCH_MQPRIO_ATTR_COUNTER) && + (mqprio->qmp_mask & SCH_MQPRIO_ATTR_HW) && + (mqprio->qmp_mask & SCH_MQPRIO_ATTR_NUM_TC) && + (mqprio->qmp_mask & SCH_MQPRIO_ATTR_PRIOMAP))) + return -NLE_NOATTR; + + opts.num_tc = mqprio->qmp_num_tc; + opts.hw = mqprio->qmp_hw; + memcpy(&opts.offset, mqprio->qmp_offset, sizeof(opts.offset)); + memcpy(&opts.count, mqprio->qmp_count, sizeof(opts.count)); + memcpy(&opts.prio_tc_map, mqprio->qmp_prio_tc_map, + sizeof(opts.prio_tc_map)); + + return nla_put(msg, TCA_OPTIONS, sizeof(opts), &opts); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set number of TCs of MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg num_tc New number of TCs. + * @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; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + mqprio->qmp_num_tc = num_tc; + mqprio->qmp_mask |= SCH_MQPRIO_ATTR_NUM_TC; +} + +/** + * Get number of TCs of MQPRIO qdisc. + * @arg qdisc MQMRIO qdisc. + * @return Number of TCs or a negative error code. + */ +int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + if (mqprio->qmp_mask & SCH_MQPRIO_ATTR_NUM_TC) + return mqprio->qmp_num_tc; + else + return -NLE_NOMEM; +} + +/** + * Set priomap of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg priomap New priority mapping. + * @arg len Length of map (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_prio_map(struct rtnl_qdisc *qdisc, + const uint8_t priomap[], int len) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX + 1)) + return -NLE_RANGE; + + memset(mqprio->qmp_prio_tc_map, 0, sizeof(mqprio->qmp_prio_tc_map)); + memcpy(mqprio->qmp_prio_tc_map, priomap, len); + mqprio->qmp_mask |= SCH_MQPRIO_ATTR_PRIOMAP; + + return 0; +} + +/** + * Get priomap of a MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc. + * @return Priority mapping as array of size TC_PRIO_MAX+1 + * or NULL if an error occurred. + */ +uint8_t *rtnl_qdisc_mqprio_get_prio_map(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + if (mqprio->qmp_mask & SCH_MQPRIO_ATTR_PRIOMAP) + return mqprio->qmp_prio_tc_map; + else + return NULL; +} + +/** + * Set usage of hardware QoS queues of a MQPRIO qdisc + * @arg qdisc MQPRIO qdisc to be modified. + * @arg hw 0 - do not use HW, 1 - use HW QoS + * @return 0 on success or a negative error code. + */ +void rtnl_qdisc_mqprio_set_hw(struct rtnl_qdisc *qdisc, int hw) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + mqprio->qmp_hw = hw; + mqprio->qmp_mask |= SCH_MQPRIO_ATTR_HW; +} + +/** + * Get value of HW QoS usage by MQPRIO qdisc. + * @arg qdisc MQMRIO qdisc. + * @return 0 - HW QoS not used, 1 - HW QoS used, or a negative error code. + */ +int rtnl_qdisc_mqprio_get_hw(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + if (mqprio->qmp_mask & SCH_MQPRIO_ATTR_HW) + return mqprio->qmp_hw; + else + return -NLE_NOMEM; +} + +/** + * Set count for queues of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg count count part of array (count@offset) + * @arg len Length of queues (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_count(struct rtnl_qdisc *qdisc, + const uint16_t count[], int len) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + if (!(mqprio->qmp_mask & SCH_MQPRIO_ATTR_NUM_TC)) + return -NLE_MISSING_ATTR; + + if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX + 1)) + return -NLE_RANGE; + + memset(mqprio->qmp_count, 0, sizeof(mqprio->qmp_count)); + memcpy(mqprio->qmp_count, count, len * sizeof(u_int16_t)); + mqprio->qmp_mask |= SCH_MQPRIO_ATTR_COUNTER; + + return 0; +} + +/** + * Get count part of queues of a MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc. + * @return Count part as array of size TC_PRIO_MAX+1 or NULL if an error + * occurred. + */ +uint16_t *rtnl_qdisc_mqprio_get_count(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + if (mqprio->qmp_mask & SCH_MQPRIO_ATTR_COUNTER) + return mqprio->qmp_count; + else + return NULL; +} + +/** + * Set offset for queues of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg offset offset part of array (count@offset) + * @arg len Length of queues (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_offset(struct rtnl_qdisc *qdisc, + const uint16_t offset[], int len) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + if (!(mqprio->qmp_mask & SCH_MQPRIO_ATTR_NUM_TC)) + return -NLE_MISSING_ATTR; + + if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX + 1)) + return -NLE_RANGE; + + memset(mqprio->qmp_offset, 0, sizeof(mqprio->qmp_offset)); + memcpy(mqprio->qmp_offset, offset, len * sizeof(u_int16_t)); + mqprio->qmp_mask |= SCH_MQPRIO_ATTR_OFFSET; + + return 0; +} + +/** + * Get offset part of queues of a MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc. + * @return Offset part as array of size up to TC_PRIO_MAX+1 or NULL if an error + * occurred. + */ +uint16_t *rtnl_qdisc_mqprio_get_offset(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + mqprio = rtnl_tc_data(TC_CAST(qdisc)); + if (!mqprio) + BUG(); + + if (mqprio->qmp_mask & SCH_MQPRIO_ATTR_OFFSET) + return mqprio->qmp_offset; + else + return NULL; +} + +/** @} */ + +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_raw = mqprio_msg_fill_raw, +}; + +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 c0f33b7..b554b87 100644 --- a/libnl-route-3.sym +++ b/libnl-route-3.sym @@ -681,6 +681,16 @@ global: rtnl_qdisc_prio_get_priomap; rtnl_qdisc_prio_set_bands; rtnl_qdisc_prio_set_priomap; + rtnl_qdisc_mqprio_set_num_tc; + rtnl_qdisc_mqprio_get_num_tc; + rtnl_qdisc_mqprio_set_prio_map; + rtnl_qdisc_mqprio_get_prio_map; + rtnl_qdisc_mqprio_set_hw; + rtnl_qdisc_mqprio_get_hw; + rtnl_qdisc_mqprio_set_count; + rtnl_qdisc_mqprio_get_count; + rtnl_qdisc_mqprio_set_offset; + rtnl_qdisc_mqprio_get_offset; rtnl_qdisc_put; rtnl_qdisc_tbf_get_limit; rtnl_qdisc_tbf_get_peakrate; -- 2.17.1