X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=drivers%2Fnet%2Fmvpp2%2Fmrvl_tm.c;fp=drivers%2Fnet%2Fmvpp2%2Fmrvl_tm.c;h=3de8997030c7c1b74c255260b48b0460b5db73f1;hb=8d01b9cd70a67cdafd5b965a70420c3bd7fb3f82;hp=0000000000000000000000000000000000000000;hpb=b63264c8342e6a1b6971c79550d2af2024b6a4de;p=deb_dpdk.git diff --git a/drivers/net/mvpp2/mrvl_tm.c b/drivers/net/mvpp2/mrvl_tm.c new file mode 100644 index 00000000..3de89970 --- /dev/null +++ b/drivers/net/mvpp2/mrvl_tm.c @@ -0,0 +1,1009 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Marvell International Ltd. + * Copyright(c) 2018 Semihalf. + * All rights reserved. + */ + +#include + +#include +#include +#include +#include + +#include "mrvl_tm.h" + +/** Minimum rate value in Bytes/s */ +#define MRVL_RATE_MIN (PP2_PPIO_MIN_CIR * 1000 / 8) + +/** Minimum burst size in Bytes */ +#define MRVL_BURST_MIN (PP2_PPIO_MIN_CBS * 1000) + +/** Maximum burst size in Bytes */ +#define MRVL_BURST_MAX 256000000 + +/** Maximum WRR weight */ +#define MRVL_WEIGHT_MAX 255 + +/** + * Get maximum port rate in Bytes/s. + * + * @param dev Pointer to the device. + * @param rate Pointer to the rate. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_get_max_rate(struct rte_eth_dev *dev, uint64_t *rate) +{ + struct ethtool_cmd edata; + struct ifreq req; + int ret, fd; + + memset(&edata, 0, sizeof(edata)); + memset(&req, 0, sizeof(req)); + edata.cmd = ETHTOOL_GSET; + strcpy(req.ifr_name, dev->data->name); + req.ifr_data = (void *)&edata; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) + return -1; + + ret = ioctl(fd, SIOCETHTOOL, &req); + if (ret == -1) { + close(fd); + return -1; + } + + close(fd); + + *rate = ethtool_cmd_speed(&edata) * 1000 * 1000 / 8; + + return 0; +} + +/** + * Initialize traffic manager related data. + * + * @param dev Pointer to the device. + * @returns 0 on success, failure otherwise. + */ +int +mrvl_tm_init(struct rte_eth_dev *dev) +{ + struct mrvl_priv *priv = dev->data->dev_private; + + LIST_INIT(&priv->shaper_profiles); + LIST_INIT(&priv->nodes); + + if (priv->rate_max) + return 0; + + return mrvl_get_max_rate(dev, &priv->rate_max); +} + +/** + * Cleanup traffic manager related data. + * + * @param dev Pointer to the device. + */ +void mrvl_tm_deinit(struct rte_eth_dev *dev) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_shaper_profile *profile = + LIST_FIRST(&priv->shaper_profiles); + struct mrvl_tm_node *node = LIST_FIRST(&priv->nodes); + + while (profile) { + struct mrvl_tm_shaper_profile *next = LIST_NEXT(profile, next); + + LIST_REMOVE(profile, next); + rte_free(profile); + profile = next; + } + + while (node) { + struct mrvl_tm_node *next = LIST_NEXT(node, next); + + LIST_REMOVE(node, next); + rte_free(node); + node = next; + } +} + +/** + * Get node using its id. + * + * @param priv Pointer to the port's private data. + * @param node_id Id used by this node. + * @returns Pointer to the node if exists, NULL otherwise. + */ +static struct mrvl_tm_node * +mrvl_node_from_id(struct mrvl_priv *priv, uint32_t node_id) +{ + struct mrvl_tm_node *node; + + LIST_FOREACH(node, &priv->nodes, next) + if (node->id == node_id) + return node; + + return NULL; +} + +/** + * Check whether node is leaf or root. + * + * @param dev Pointer to the device. + * @param node_id Id used by this node. + * @param is_leaf Pointer to flag indicating whether node is a leaf. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_type_get(struct rte_eth_dev *dev, uint32_t node_id, int *is_leaf, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_node *node; + + if (!is_leaf) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, NULL); + + node = mrvl_node_from_id(priv, node_id); + if (!node) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id does not exist\n"); + + *is_leaf = node->type == MRVL_NODE_QUEUE ? 1 : 0; + + return 0; +} + +/** + * Get traffic manager capabilities. + * + * @param dev Pointer to the device (unused). + * @param cap Pointer to the capabilities. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_capabilities_get(struct rte_eth_dev *dev, + struct rte_tm_capabilities *cap, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + + if (!cap) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, "Capabilities are missing\n"); + + memset(cap, 0, sizeof(*cap)); + + cap->n_nodes_max = 1 + dev->data->nb_tx_queues; /* port + txqs number */ + cap->n_levels_max = 2; /* port level + txqs level */ + cap->non_leaf_nodes_identical = 1; + cap->leaf_nodes_identical = 1; + + cap->shaper_n_max = cap->n_nodes_max; + cap->shaper_private_n_max = cap->shaper_n_max; + cap->shaper_private_rate_min = MRVL_RATE_MIN; + cap->shaper_private_rate_max = priv->rate_max; + + cap->sched_n_children_max = dev->data->nb_tx_queues; + cap->sched_sp_n_priorities_max = dev->data->nb_tx_queues; + cap->sched_wfq_n_children_per_group_max = dev->data->nb_tx_queues; + cap->sched_wfq_n_groups_max = 1; + cap->sched_wfq_weight_max = MRVL_WEIGHT_MAX; + + cap->dynamic_update_mask = RTE_TM_UPDATE_NODE_SUSPEND_RESUME | + RTE_TM_UPDATE_NODE_STATS; + cap->stats_mask = RTE_TM_STATS_N_PKTS | RTE_TM_STATS_N_BYTES; + + return 0; +} + +/** + * Get traffic manager hierarchy level capabilities. + * + * @param dev Pointer to the device. + * @param level_id Id of the level. + * @param cap Pointer to the level capabilities. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_level_capabilities_get(struct rte_eth_dev *dev, + uint32_t level_id, + struct rte_tm_level_capabilities *cap, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + + if (!cap) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, NULL); + + memset(cap, 0, sizeof(*cap)); + + if (level_id != MRVL_NODE_PORT && level_id != MRVL_NODE_QUEUE) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_LEVEL_ID, + NULL, "Wrong level id\n"); + + if (level_id == MRVL_NODE_PORT) { + cap->n_nodes_max = 1; + cap->n_nodes_nonleaf_max = 1; + cap->non_leaf_nodes_identical = 1; + + cap->nonleaf.shaper_private_supported = 1; + cap->nonleaf.shaper_private_rate_min = MRVL_RATE_MIN; + cap->nonleaf.shaper_private_rate_max = priv->rate_max; + + cap->nonleaf.sched_n_children_max = dev->data->nb_tx_queues; + cap->nonleaf.sched_sp_n_priorities_max = 1; + cap->nonleaf.sched_wfq_n_children_per_group_max = + dev->data->nb_tx_queues; + cap->nonleaf.sched_wfq_n_groups_max = 1; + cap->nonleaf.sched_wfq_weight_max = MRVL_WEIGHT_MAX; + cap->nonleaf.stats_mask = RTE_TM_STATS_N_PKTS | + RTE_TM_STATS_N_BYTES; + } else { /* level_id == MRVL_NODE_QUEUE */ + cap->n_nodes_max = dev->data->nb_tx_queues; + cap->n_nodes_leaf_max = dev->data->nb_tx_queues; + cap->leaf_nodes_identical = 1; + + cap->leaf.shaper_private_supported = 1; + cap->leaf.shaper_private_rate_min = MRVL_RATE_MIN; + cap->leaf.shaper_private_rate_max = priv->rate_max; + cap->leaf.stats_mask = RTE_TM_STATS_N_PKTS; + } + + return 0; +} + +/** + * Get node capabilities. + * + * @param dev Pointer to the device. + * @param node_id Id of the node. + * @param cap Pointer to the capabilities. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_capabilities_get(struct rte_eth_dev *dev, uint32_t node_id, + struct rte_tm_node_capabilities *cap, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_node *node; + + if (!cap) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, NULL); + + memset(cap, 0, sizeof(*cap)); + + node = mrvl_node_from_id(priv, node_id); + if (!node) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id does not exist\n"); + + cap->shaper_private_supported = 1; + cap->shaper_private_rate_min = MRVL_RATE_MIN; + cap->shaper_private_rate_max = priv->rate_max; + + if (node->type == MRVL_NODE_PORT) { + cap->nonleaf.sched_n_children_max = dev->data->nb_tx_queues; + cap->nonleaf.sched_sp_n_priorities_max = 1; + cap->nonleaf.sched_wfq_n_children_per_group_max = + dev->data->nb_tx_queues; + cap->nonleaf.sched_wfq_n_groups_max = 1; + cap->nonleaf.sched_wfq_weight_max = MRVL_WEIGHT_MAX; + cap->stats_mask = RTE_TM_STATS_N_PKTS | RTE_TM_STATS_N_BYTES; + } else { + cap->stats_mask = RTE_TM_STATS_N_PKTS; + } + + return 0; +} + +/** + * Get shaper profile using its id. + * + * @param priv Pointer to the port's private data. + * @param shaper_profile_id Id used by the shaper. + * @returns Pointer to the shaper profile if exists, NULL otherwise. + */ +static struct mrvl_tm_shaper_profile * +mrvl_shaper_profile_from_id(struct mrvl_priv *priv, uint32_t shaper_profile_id) +{ + struct mrvl_tm_shaper_profile *profile; + + LIST_FOREACH(profile, &priv->shaper_profiles, next) + if (profile->id == shaper_profile_id) + return profile; + + return NULL; +} + +/** + * Add a new shaper profile. + * + * @param dev Pointer to the device. + * @param shaper_profile_id Id of the new profile. + * @param params Pointer to the shaper profile parameters. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_shaper_profile_add(struct rte_eth_dev *dev, uint32_t shaper_profile_id, + struct rte_tm_shaper_params *params, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_shaper_profile *profile; + + if (!params) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, NULL); + + if (params->committed.rate) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_RATE, + NULL, "Committed rate not supported\n"); + + if (params->committed.size) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_SIZE, + NULL, "Committed bucket size not supported\n"); + + if (params->peak.rate < MRVL_RATE_MIN || + params->peak.rate > priv->rate_max) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_RATE, + NULL, "Peak rate is out of range\n"); + + if (params->peak.size < MRVL_BURST_MIN || + params->peak.size > MRVL_BURST_MAX) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_SIZE, + NULL, "Peak size is out of range\n"); + + if (shaper_profile_id == RTE_TM_SHAPER_PROFILE_ID_NONE) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID, + NULL, "Wrong shaper profile id\n"); + + profile = mrvl_shaper_profile_from_id(priv, shaper_profile_id); + if (profile) + return -rte_tm_error_set(error, EEXIST, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID, + NULL, "Profile id already exists\n"); + + profile = rte_zmalloc_socket(NULL, sizeof(*profile), 0, + rte_socket_id()); + if (!profile) + return -rte_tm_error_set(error, ENOMEM, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, NULL); + + profile->id = shaper_profile_id; + rte_memcpy(&profile->params, params, sizeof(profile->params)); + + LIST_INSERT_HEAD(&priv->shaper_profiles, profile, next); + + return 0; +} + +/** + * Remove a shaper profile. + * + * @param dev Pointer to the device. + * @param shaper_profile_id Id of the shaper profile. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_shaper_profile_delete(struct rte_eth_dev *dev, uint32_t shaper_profile_id, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_shaper_profile *profile; + + profile = mrvl_shaper_profile_from_id(priv, shaper_profile_id); + if (!profile) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID, + NULL, "Profile id does not exist\n"); + + if (profile->refcnt) + return -rte_tm_error_set(error, EPERM, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID, + NULL, "Profile is used\n"); + + LIST_REMOVE(profile, next); + rte_free(profile); + + return 0; +} + +/** + * Check node parameters. + * + * @param dev Pointer to the device. + * @param node_id Id used by the node. + * @param priority Priority value. + * @param weight Weight value. + * @param level_id Id of the level. + * @param params Pointer to the node parameters. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_check_params(struct rte_eth_dev *dev, uint32_t node_id, + uint32_t priority, uint32_t weight, uint32_t level_id, + struct rte_tm_node_params *params, + struct rte_tm_error *error) +{ + if (node_id == RTE_TM_NODE_ID_NULL) + return -rte_tm_error_set(error, EINVAL, RTE_TM_NODE_ID_NULL, + NULL, "Node id is invalid\n"); + + if (priority) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PRIORITY, + NULL, "Priority should be 0\n"); + + if (weight > MRVL_WEIGHT_MAX) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_WEIGHT, + NULL, "Weight is out of range\n"); + + if (level_id != MRVL_NODE_PORT && level_id != MRVL_NODE_QUEUE) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_LEVEL_ID, + NULL, "Wrong level id\n"); + + if (!params) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, NULL); + + if (params->shared_shaper_id) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_SHARED_SHAPER_ID, + NULL, "Shared shaper is not supported\n"); + + if (params->n_shared_shapers) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SHARED_SHAPERS, + NULL, "Shared shaper is not supported\n"); + + /* verify port (root node) settings */ + if (node_id >= dev->data->nb_tx_queues) { + if (params->nonleaf.wfq_weight_mode) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_WFQ_WEIGHT_MODE, + NULL, "WFQ is not supported\n"); + + if (params->nonleaf.n_sp_priorities != 1) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SP_PRIORITIES, + NULL, "SP is not supported\n"); + + if (params->stats_mask & ~(RTE_TM_STATS_N_PKTS | + RTE_TM_STATS_N_BYTES)) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_STATS, + NULL, + "Requested port stats are not supported\n"); + + return 0; + } + + /* verify txq (leaf node) settings */ + if (params->leaf.cman) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_CMAN, + NULL, + "Congestion mngmt is not supported\n"); + + if (params->leaf.wred.wred_profile_id) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_WRED_PROFILE_ID, + NULL, "WRED is not supported\n"); + + if (params->leaf.wred.shared_wred_context_id) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_SHARED_WRED_CONTEXT_ID, + NULL, "WRED is not supported\n"); + + if (params->leaf.wred.n_shared_wred_contexts) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SHARED_WRED_CONTEXTS, + NULL, "WRED is not supported\n"); + + if (params->stats_mask & ~RTE_TM_STATS_N_PKTS) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_STATS, + NULL, + "Requested txq stats are not supported\n"); + + return 0; +} + +/** + * Add a new node. + * + * @param dev Pointer to the device. + * @param node_id Id of the node. + * @param parent_node_id Id of the parent node. + * @param priority Priority value. + * @param weight Weight value. + * @param level_id Id of the level. + * @param params Pointer to the node parameters. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_add(struct rte_eth_dev *dev, uint32_t node_id, + uint32_t parent_node_id, uint32_t priority, uint32_t weight, + uint32_t level_id, struct rte_tm_node_params *params, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_shaper_profile *profile = NULL; + struct mrvl_tm_node *node, *parent = NULL; + int ret; + + if (priv->ppio) + return -rte_tm_error_set(error, EPERM, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, "Port is already started\n"); + + ret = mrvl_node_check_params(dev, node_id, priority, weight, level_id, + params, error); + if (ret) + return ret; + + if (params->shaper_profile_id != RTE_TM_SHAPER_PROFILE_ID_NONE) { + profile = mrvl_shaper_profile_from_id(priv, + params->shaper_profile_id); + if (!profile) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID, + NULL, "Shaper id does not exist\n"); + } + + if (parent_node_id == RTE_TM_NODE_ID_NULL) { + LIST_FOREACH(node, &priv->nodes, next) { + if (node->type != MRVL_NODE_PORT) + continue; + + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, "Root node exists\n"); + } + } else { + parent = mrvl_node_from_id(priv, parent_node_id); + if (!parent) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID, + NULL, "Node id does not exist\n"); + } + + node = mrvl_node_from_id(priv, node_id); + if (node) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id already exists\n"); + + node = rte_zmalloc_socket(NULL, sizeof(*node), 0, rte_socket_id()); + if (!node) + return -rte_tm_error_set(error, ENOMEM, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, NULL); + + node->id = node_id; + node->type = parent_node_id == RTE_TM_NODE_ID_NULL ? MRVL_NODE_PORT : + MRVL_NODE_QUEUE; + + if (parent) { + node->parent = parent; + parent->refcnt++; + } + + if (profile) { + node->profile = profile; + profile->refcnt++; + } + + node->weight = weight; + node->stats_mask = params->stats_mask; + + LIST_INSERT_HEAD(&priv->nodes, node, next); + + return 0; +} + +/** + * Delete a node. + * + * @param dev Pointer to the device. + * @param node_id Id of the node. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_delete(struct rte_eth_dev *dev, uint32_t node_id, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_node *node; + + if (priv->ppio) { + return -rte_tm_error_set(error, EPERM, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, "Port is already started\n"); + } + + node = mrvl_node_from_id(priv, node_id); + if (!node) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id does not exist\n"); + + if (node->refcnt) + return -rte_tm_error_set(error, EPERM, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id is used\n"); + + if (node->parent) + node->parent->refcnt--; + + if (node->profile) + node->profile->refcnt--; + + LIST_REMOVE(node, next); + rte_free(node); + + return 0; +} + +/** + * Helper for suspending specific tx queue. + * + * @param dev Pointer to the device. + * @param node_id Id used by this node. + * @returns 0 on success, negative value otherwise. + */ +static int mrvl_node_suspend_one(struct rte_eth_dev *dev, uint32_t node_id, + struct rte_tm_error *error) +{ + int ret = dev->dev_ops->tx_queue_stop(dev, node_id); + if (ret) + return -rte_tm_error_set(error, ret, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, "Failed to suspend a txq\n"); + + return 0; +} + +/** + * Suspend a node. + * + * @param dev Pointer to the device. + * @param node_id Id of the node. + * @param error Pointer to the error. + * returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_suspend(struct rte_eth_dev *dev, uint32_t node_id, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_node *node, *tmp; + int ret; + + node = mrvl_node_from_id(priv, node_id); + if (!node) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id does not exist\n"); + + if (!node->parent) { + LIST_FOREACH(tmp, &priv->nodes, next) { + if (!tmp->parent) + continue; + + if (node != tmp->parent) + continue; + + ret = mrvl_node_suspend_one(dev, tmp->id, error); + if (ret) + return ret; + } + + return 0; + } + + return mrvl_node_suspend_one(dev, node_id, error); +} + +/** + * Resume a node. + * + * @param dev Pointer to the device. + * @param node_id Id of the node. + * @param error Pointer to the error. + * returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_resume(struct rte_eth_dev *dev, uint32_t node_id, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_node *node; + int ret; + + node = mrvl_node_from_id(priv, node_id); + if (!node) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id does not exist\n"); + + + if (!node->parent) + return -rte_tm_error_set(error, EPERM, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Cannot suspend a port\n"); + + ret = dev->dev_ops->tx_queue_start(dev, node_id); + if (ret) + return -rte_tm_error_set(error, ret, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, "Failed to resume a txq\n"); + return 0; +} + +/** + * Apply traffic manager hierarchy. + * + * @param dev Pointer to the device. + * @param clear_on_fail Flag indicating whether to do cleanup on the failure. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_hierarchy_commit(struct rte_eth_dev *dev, int clear_on_fail, + struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_node *node; + int ret; + + if (priv->ppio) { + ret = -rte_tm_error_set(error, EPERM, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, "Port is already started\n"); + goto out; + } + + LIST_FOREACH(node, &priv->nodes, next) { + struct pp2_ppio_outq_params *p; + + if (node->type == MRVL_NODE_PORT) { + if (!node->profile) + continue; + + priv->ppio_params.rate_limit_enable = 1; + priv->ppio_params.rate_limit_params.cir = + node->profile->params.peak.rate * 8 / 1000; + priv->ppio_params.rate_limit_params.cbs = + node->profile->params.peak.size / 1000; + + MRVL_LOG(INFO, + "Port rate limit overrides txqs rate limit"); + + continue; + } + + if (node->id >= dev->data->nb_tx_queues) { + ret = -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_ID, NULL, + "Not enough txqs are configured\n"); + goto out; + } + + p = &priv->ppio_params.outqs_params.outqs_params[node->id]; + + if (node->weight) { + p->sched_mode = PP2_PPIO_SCHED_M_WRR; + p->weight = node->weight; + } else { + p->sched_mode = PP2_PPIO_SCHED_M_SP; + p->weight = 0; + } + + if (node->profile) { + p->rate_limit_enable = 1; + /* convert Bytes/s to kilo bits/s */ + p->rate_limit_params.cir = + node->profile->params.peak.rate * 8 / 1000; + /* convert bits to kilo bits */ + p->rate_limit_params.cbs = + node->profile->params.peak.size / 1000; + } else { + p->rate_limit_enable = 0; + p->rate_limit_params.cir = 0; + p->rate_limit_params.cbs = 0; + } + } + + /* reset to defaults in case applied tm hierarchy is empty */ + if (LIST_EMPTY(&priv->nodes)) { + int i; + + for (i = 0; i < priv->ppio_params.outqs_params.num_outqs; i++) { + struct pp2_ppio_outq_params *p = + &priv->ppio_params.outqs_params.outqs_params[i]; + + p->sched_mode = PP2_PPIO_SCHED_M_WRR; + p->weight = 0; + p->rate_limit_enable = 0; + p->rate_limit_params.cir = 0; + p->rate_limit_params.cbs = 0; + } + } + + return 0; +out: + if (clear_on_fail) { + mrvl_tm_deinit(dev); + mrvl_tm_init(dev); + } + + return ret; +} + +/** + * Read statistics counters for current node. + * + * @param dev Pointer to the device. + * @param node_id Id of the node. + * @param stats Pointer to the statistics counters. + * @param stats_mask Pointer to mask of enabled statistics counters + * that are retrieved. + * @param clear Flag indicating whether to clear statistics. + * Non-zero value clears statistics. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_stats_read(struct rte_eth_dev *dev, uint32_t node_id, + struct rte_tm_node_stats *stats, uint64_t *stats_mask, + int clear, struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_node *node; + int ret; + + if (!priv->ppio) { + return -rte_tm_error_set(error, EPERM, + RTE_TM_ERROR_TYPE_UNSPECIFIED, + NULL, "Port is not started\n"); + } + + node = mrvl_node_from_id(priv, node_id); + if (!node) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id does not exist\n"); + + if (stats_mask) + *stats_mask = node->stats_mask; + + if (!stats) + return 0; + + memset(stats, 0, sizeof(*stats)); + + if (!node->parent) { + struct pp2_ppio_statistics s; + + memset(&s, 0, sizeof(s)); + ret = pp2_ppio_get_statistics(priv->ppio, &s, clear); + if (ret) + return -rte_tm_error_set(error, -ret, + RTE_TM_ERROR_TYPE_UNSPECIFIED, NULL, + "Failed to read port statistics\n"); + + if (node->stats_mask & RTE_TM_STATS_N_PKTS) + stats->n_pkts = s.tx_packets; + + if (node->stats_mask & RTE_TM_STATS_N_BYTES) + stats->n_bytes = s.tx_bytes; + } else { + struct pp2_ppio_outq_statistics s; + + memset(&s, 0, sizeof(s)); + ret = pp2_ppio_outq_get_statistics(priv->ppio, node_id, &s, + clear); + if (ret) + return -rte_tm_error_set(error, -ret, + RTE_TM_ERROR_TYPE_UNSPECIFIED, NULL, + "Failed to read txq statistics\n"); + + if (node->stats_mask & RTE_TM_STATS_N_PKTS) + stats->n_pkts = s.deq_desc; + } + + return 0; +} + +/** + * Update node statistics. + * + * @param dev Pointer to the device. + * @param node_id Id of the node. + * @param stats_mask Bitmask of statistics counters to be enabled. + * @param error Pointer to the error. + * @returns 0 on success, negative value otherwise. + */ +static int +mrvl_node_stats_update(struct rte_eth_dev *dev, uint32_t node_id, + uint64_t stats_mask, struct rte_tm_error *error) +{ + struct mrvl_priv *priv = dev->data->dev_private; + struct mrvl_tm_node *node; + + node = mrvl_node_from_id(priv, node_id); + if (!node) + return -rte_tm_error_set(error, ENODEV, + RTE_TM_ERROR_TYPE_NODE_ID, + NULL, "Node id does not exist\n"); + + if (!node->parent) { + if (stats_mask & ~(RTE_TM_STATS_N_PKTS | RTE_TM_STATS_N_BYTES)) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_STATS, + NULL, + "Requested port stats are not supported\n"); + } else { + if (stats_mask & ~RTE_TM_STATS_N_PKTS) + return -rte_tm_error_set(error, EINVAL, + RTE_TM_ERROR_TYPE_NODE_PARAMS_STATS, + NULL, + "Requested txq stats are not supported\n"); + } + + node->stats_mask = stats_mask; + + return 0; +} + +const struct rte_tm_ops mrvl_tm_ops = { + .node_type_get = mrvl_node_type_get, + .capabilities_get = mrvl_capabilities_get, + .level_capabilities_get = mrvl_level_capabilities_get, + .node_capabilities_get = mrvl_node_capabilities_get, + .shaper_profile_add = mrvl_shaper_profile_add, + .shaper_profile_delete = mrvl_shaper_profile_delete, + .node_add = mrvl_node_add, + .node_delete = mrvl_node_delete, + .node_suspend = mrvl_node_suspend, + .node_resume = mrvl_node_resume, + .hierarchy_commit = mrvl_hierarchy_commit, + .node_stats_update = mrvl_node_stats_update, + .node_stats_read = mrvl_node_stats_read, +};