Add support to add/delete nodes in a hierarchy.
This patch also adds misc utils to get node name,
walk through nodes etc.
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Satha Rao <skoteshwar@marvell.com>
/* Traffic Management */
#define ROC_NIX_TM_MAX_SCHED_WT ((uint8_t)~0)
+#define ROC_NIX_TM_SHAPER_PROFILE_NONE UINT32_MAX
+#define ROC_NIX_TM_NODE_ID_INVALID UINT32_MAX
enum roc_nix_tm_tree {
ROC_NIX_TM_DEFAULT = 0,
int __roc_api roc_nix_tm_sq_aura_fc(struct roc_nix_sq *sq, bool enable);
int __roc_api roc_nix_tm_sq_flush_spin(struct roc_nix_sq *sq);
+/*
+ * TM User hierarchy API.
+ */
+
+struct roc_nix_tm_node {
+#define ROC_NIX_TM_NODE_SZ (128)
+ uint8_t reserved[ROC_NIX_TM_NODE_SZ];
+
+ uint32_t id;
+ uint32_t parent_id;
+ uint32_t priority;
+ uint32_t weight;
+ uint32_t shaper_profile_id;
+ uint16_t lvl;
+ bool pkt_mode;
+ bool pkt_mode_set;
+ /* Function to free this memory */
+ void (*free_fn)(void *node);
+};
+
+int __roc_api roc_nix_tm_node_add(struct roc_nix *roc_nix,
+ struct roc_nix_tm_node *roc_node);
+int __roc_api roc_nix_tm_node_delete(struct roc_nix *roc_nix, uint32_t node_id,
+ bool free);
+int __roc_api roc_nix_tm_node_pkt_mode_update(struct roc_nix *roc_nix,
+ uint32_t node_id, bool pkt_mode);
+
+struct roc_nix_tm_node *__roc_api roc_nix_tm_node_get(struct roc_nix *roc_nix,
+ uint32_t node_id);
+struct roc_nix_tm_node *__roc_api
+roc_nix_tm_node_next(struct roc_nix *roc_nix, struct roc_nix_tm_node *__prev);
+
+/*
+ * TM utilities API.
+ */
+int __roc_api roc_nix_tm_node_lvl(struct roc_nix *roc_nix, uint32_t node_id);
+int __roc_api roc_nix_tm_node_name_get(struct roc_nix *roc_nix,
+ uint32_t node_id, char *buf,
+ size_t buflen);
+
/* MAC */
int __roc_api roc_nix_mac_rxtx_start_stop(struct roc_nix *roc_nix, bool start);
int __roc_api roc_nix_mac_link_event_start_stop(struct roc_nix *roc_nix,
int nix_tm_sq_flush_pre(struct roc_nix_sq *sq);
int nix_tm_sq_flush_post(struct roc_nix_sq *sq);
int nix_tm_smq_xoff(struct nix *nix, struct nix_tm_node *node, bool enable);
+int nix_tm_node_add(struct roc_nix *roc_nix, struct nix_tm_node *node);
+int nix_tm_node_delete(struct roc_nix *roc_nix, uint32_t node_id,
+ enum roc_nix_tm_tree tree, bool free);
+int nix_tm_free_node_resource(struct nix *nix, struct nix_tm_node *node);
int nix_tm_clear_path_xoff(struct nix *nix, struct nix_tm_node *node);
/*
* TM priv utils.
*/
+uint16_t nix_tm_lvl2nix(struct nix *nix, uint32_t lvl);
+uint16_t nix_tm_lvl2nix_tl1_root(uint32_t lvl);
+uint16_t nix_tm_lvl2nix_tl2_root(uint32_t lvl);
+uint16_t nix_tm_resource_avail(struct nix *nix, uint8_t hw_lvl, bool contig);
+int nix_tm_validate_prio(struct nix *nix, uint32_t lvl, uint32_t parent_id,
+ uint32_t priority, enum roc_nix_tm_tree tree);
struct nix_tm_node *nix_tm_node_search(struct nix *nix, uint32_t node_id,
enum roc_nix_tm_tree tree);
+struct nix_tm_shaper_profile *nix_tm_shaper_profile_search(struct nix *nix,
+ uint32_t id);
uint8_t nix_tm_sw_xoff_prep(struct nix_tm_node *node, bool enable,
volatile uint64_t *reg, volatile uint64_t *regval);
+struct nix_tm_node *nix_tm_node_alloc(void);
+void nix_tm_node_free(struct nix_tm_node *node);
#endif /* _ROC_NIX_PRIV_H_ */
#include "roc_api.h"
#include "roc_priv.h"
+int
+nix_tm_node_add(struct roc_nix *roc_nix, struct nix_tm_node *node)
+{
+ struct nix *nix = roc_nix_to_nix_priv(roc_nix);
+ struct nix_tm_shaper_profile *profile;
+ uint32_t node_id, parent_id, lvl;
+ struct nix_tm_node *parent_node;
+ uint32_t priority, profile_id;
+ uint8_t hw_lvl, exp_next_lvl;
+ enum roc_nix_tm_tree tree;
+ int rc;
+
+ node_id = node->id;
+ priority = node->priority;
+ parent_id = node->parent_id;
+ profile_id = node->shaper_profile_id;
+ lvl = node->lvl;
+ tree = node->tree;
+
+ plt_tm_dbg("Add node %s lvl %u id %u, prio 0x%x weight 0x%x "
+ "parent %u profile 0x%x tree %u",
+ nix_tm_hwlvl2str(nix_tm_lvl2nix(nix, lvl)), lvl, node_id,
+ priority, node->weight, parent_id, profile_id, tree);
+
+ if (tree >= ROC_NIX_TM_TREE_MAX)
+ return NIX_ERR_PARAM;
+
+ /* Translate sw level id's to nix hw level id's */
+ hw_lvl = nix_tm_lvl2nix(nix, lvl);
+ if (hw_lvl == NIX_TXSCH_LVL_CNT && !nix_tm_is_leaf(nix, lvl))
+ return NIX_ERR_TM_INVALID_LVL;
+
+ /* Leaf nodes have to be same priority */
+ if (nix_tm_is_leaf(nix, lvl) && priority != 0)
+ return NIX_ERR_TM_INVALID_PRIO;
+
+ parent_node = nix_tm_node_search(nix, parent_id, tree);
+
+ if (node_id < nix->nb_tx_queues)
+ exp_next_lvl = NIX_TXSCH_LVL_SMQ;
+ else
+ exp_next_lvl = hw_lvl + 1;
+
+ /* Check if there is no parent node yet */
+ if (hw_lvl != nix->tm_root_lvl &&
+ (!parent_node || parent_node->hw_lvl != exp_next_lvl))
+ return NIX_ERR_TM_INVALID_PARENT;
+
+ /* Check if a node already exists */
+ if (nix_tm_node_search(nix, node_id, tree))
+ return NIX_ERR_TM_NODE_EXISTS;
+
+ profile = nix_tm_shaper_profile_search(nix, profile_id);
+ if (!nix_tm_is_leaf(nix, lvl)) {
+ /* Check if shaper profile exists for non leaf node */
+ if (!profile && profile_id != ROC_NIX_TM_SHAPER_PROFILE_NONE)
+ return NIX_ERR_TM_INVALID_SHAPER_PROFILE;
+
+ /* Packet mode in profile should match with that of tm node */
+ if (profile && profile->pkt_mode != node->pkt_mode)
+ return NIX_ERR_TM_PKT_MODE_MISMATCH;
+ }
+
+ /* Check if there is second DWRR already in siblings or holes in prio */
+ rc = nix_tm_validate_prio(nix, lvl, parent_id, priority, tree);
+ if (rc)
+ return rc;
+
+ if (node->weight > ROC_NIX_TM_MAX_SCHED_WT)
+ return NIX_ERR_TM_WEIGHT_EXCEED;
+
+ /* Maintain minimum weight */
+ if (!node->weight)
+ node->weight = 1;
+
+ node->hw_lvl = nix_tm_lvl2nix(nix, lvl);
+ node->rr_prio = 0xF;
+ node->max_prio = UINT32_MAX;
+ node->hw_id = NIX_TM_HW_ID_INVALID;
+ node->flags = 0;
+
+ if (profile)
+ profile->ref_cnt++;
+
+ node->parent = parent_node;
+ if (parent_node)
+ parent_node->child_realloc = true;
+ node->parent_hw_id = NIX_TM_HW_ID_INVALID;
+
+ TAILQ_INSERT_TAIL(&nix->trees[tree], node, node);
+ plt_tm_dbg("Added node %s lvl %u id %u (%p)",
+ nix_tm_hwlvl2str(node->hw_lvl), lvl, node_id, node);
+ return 0;
+}
int
nix_tm_clear_path_xoff(struct nix *nix, struct nix_tm_node *node)
return 0;
}
+int
+nix_tm_free_node_resource(struct nix *nix, struct nix_tm_node *node)
+{
+ struct mbox *mbox = (&nix->dev)->mbox;
+ struct nix_txsch_free_req *req;
+ struct plt_bitmap *bmp;
+ uint16_t avail, hw_id;
+ uint8_t hw_lvl;
+ int rc = -ENOSPC;
+
+ hw_lvl = node->hw_lvl;
+ hw_id = node->hw_id;
+ bmp = nix->schq_bmp[hw_lvl];
+ /* Free specific HW resource */
+ plt_tm_dbg("Free hwres %s(%u) lvl %u id %u (%p)",
+ nix_tm_hwlvl2str(node->hw_lvl), hw_id, node->lvl, node->id,
+ node);
+
+ avail = nix_tm_resource_avail(nix, hw_lvl, false);
+ /* Always for now free to discontiguous queue when avail
+ * is not sufficient.
+ */
+ if (nix->discontig_rsvd[hw_lvl] &&
+ avail < nix->discontig_rsvd[hw_lvl]) {
+ PLT_ASSERT(hw_id < NIX_TM_MAX_HW_TXSCHQ);
+ PLT_ASSERT(plt_bitmap_get(bmp, hw_id) == 0);
+ plt_bitmap_set(bmp, hw_id);
+ node->hw_id = NIX_TM_HW_ID_INVALID;
+ node->flags &= ~NIX_TM_NODE_HWRES;
+ return 0;
+ }
+
+ /* Free to AF */
+ req = mbox_alloc_msg_nix_txsch_free(mbox);
+ if (req == NULL)
+ return rc;
+ req->flags = 0;
+ req->schq_lvl = node->hw_lvl;
+ req->schq = hw_id;
+ rc = mbox_process(mbox);
+ if (rc) {
+ plt_err("failed to release hwres %s(%u) rc %d",
+ nix_tm_hwlvl2str(node->hw_lvl), hw_id, rc);
+ return rc;
+ }
+
+ /* Mark parent as dirty for reallocing it's children */
+ if (node->parent)
+ node->parent->child_realloc = true;
+
+ node->hw_id = NIX_TM_HW_ID_INVALID;
+ node->flags &= ~NIX_TM_NODE_HWRES;
+ plt_tm_dbg("Released hwres %s(%u) to af",
+ nix_tm_hwlvl2str(node->hw_lvl), hw_id);
+ return 0;
+}
+
+int
+nix_tm_node_delete(struct roc_nix *roc_nix, uint32_t node_id,
+ enum roc_nix_tm_tree tree, bool free)
+{
+ struct nix *nix = roc_nix_to_nix_priv(roc_nix);
+ struct nix_tm_shaper_profile *profile;
+ struct nix_tm_node *node, *child;
+ struct nix_tm_node_list *list;
+ uint32_t profile_id;
+ int rc;
+
+ plt_tm_dbg("Delete node id %u tree %u", node_id, tree);
+
+ node = nix_tm_node_search(nix, node_id, tree);
+ if (!node)
+ return NIX_ERR_TM_INVALID_NODE;
+
+ list = nix_tm_node_list(nix, tree);
+ /* Check for any existing children */
+ TAILQ_FOREACH(child, list, node) {
+ if (child->parent == node)
+ return NIX_ERR_TM_CHILD_EXISTS;
+ }
+
+ /* Remove shaper profile reference */
+ profile_id = node->shaper_profile_id;
+ profile = nix_tm_shaper_profile_search(nix, profile_id);
+
+ /* Free hw resource locally */
+ if (node->flags & NIX_TM_NODE_HWRES) {
+ rc = nix_tm_free_node_resource(nix, node);
+ if (rc)
+ return rc;
+ }
+
+ if (profile)
+ profile->ref_cnt--;
+
+ TAILQ_REMOVE(list, node, node);
+
+ plt_tm_dbg("Deleted node %s lvl %u id %u, prio 0x%x weight 0x%x "
+ "parent %u profile 0x%x tree %u (%p)",
+ nix_tm_hwlvl2str(node->hw_lvl), node->lvl, node->id,
+ node->priority, node->weight,
+ node->parent ? node->parent->id : UINT32_MAX,
+ node->shaper_profile_id, tree, node);
+ /* Free only if requested */
+ if (free)
+ nix_tm_node_free(node);
+ return 0;
+}
+
int
nix_tm_conf_init(struct roc_nix *roc_nix)
{
void *bmp_mem;
int rc, i;
+ PLT_STATIC_ASSERT(sizeof(struct nix_tm_node) <= ROC_NIX_TM_NODE_SZ);
+
nix->tm_flags = 0;
for (i = 0; i < ROC_NIX_TM_TREE_MAX; i++)
TAILQ_INIT(&nix->trees[i]);
plt_wmb();
return 0;
}
+
+int
+roc_nix_tm_node_add(struct roc_nix *roc_nix, struct roc_nix_tm_node *roc_node)
+{
+ struct nix_tm_node *node;
+
+ node = (struct nix_tm_node *)&roc_node->reserved;
+ node->id = roc_node->id;
+ node->priority = roc_node->priority;
+ node->weight = roc_node->weight;
+ node->lvl = roc_node->lvl;
+ node->parent_id = roc_node->parent_id;
+ node->shaper_profile_id = roc_node->shaper_profile_id;
+ node->pkt_mode = roc_node->pkt_mode;
+ node->pkt_mode_set = roc_node->pkt_mode_set;
+ node->free_fn = roc_node->free_fn;
+ node->tree = ROC_NIX_TM_USER;
+
+ return nix_tm_node_add(roc_nix, node);
+}
+
+int
+roc_nix_tm_node_pkt_mode_update(struct roc_nix *roc_nix, uint32_t node_id,
+ bool pkt_mode)
+{
+ struct nix *nix = roc_nix_to_nix_priv(roc_nix);
+ struct nix_tm_node *node, *child;
+ struct nix_tm_node_list *list;
+ int num_children = 0;
+
+ node = nix_tm_node_search(nix, node_id, ROC_NIX_TM_USER);
+ if (!node)
+ return NIX_ERR_TM_INVALID_NODE;
+
+ if (node->pkt_mode == pkt_mode) {
+ node->pkt_mode_set = true;
+ return 0;
+ }
+
+ /* Check for any existing children, if there are any,
+ * then we cannot update the pkt mode as children's quantum
+ * are already taken in.
+ */
+ list = nix_tm_node_list(nix, ROC_NIX_TM_USER);
+ TAILQ_FOREACH(child, list, node) {
+ if (child->parent == node)
+ num_children++;
+ }
+
+ /* Cannot update mode if it has children or tree is enabled */
+ if ((nix->tm_flags & NIX_TM_HIERARCHY_ENA) && num_children)
+ return -EBUSY;
+
+ if (node->pkt_mode_set && num_children)
+ return NIX_ERR_TM_PKT_MODE_MISMATCH;
+
+ node->pkt_mode = pkt_mode;
+ node->pkt_mode_set = true;
+
+ return 0;
+}
+
+int
+roc_nix_tm_node_name_get(struct roc_nix *roc_nix, uint32_t node_id, char *buf,
+ size_t buflen)
+{
+ struct nix *nix = roc_nix_to_nix_priv(roc_nix);
+ struct nix_tm_node *node;
+
+ node = nix_tm_node_search(nix, node_id, ROC_NIX_TM_USER);
+ if (!node) {
+ plt_strlcpy(buf, "???", buflen);
+ return NIX_ERR_TM_INVALID_NODE;
+ }
+
+ if (node->hw_lvl == NIX_TXSCH_LVL_CNT)
+ snprintf(buf, buflen, "SQ_%d", node->id);
+ else
+ snprintf(buf, buflen, "%s_%d", nix_tm_hwlvl2str(node->hw_lvl),
+ node->hw_id);
+ return 0;
+}
+
+int
+roc_nix_tm_node_delete(struct roc_nix *roc_nix, uint32_t node_id, bool free)
+{
+ return nix_tm_node_delete(roc_nix, node_id, ROC_NIX_TM_USER, free);
+}
#include "roc_api.h"
#include "roc_priv.h"
+uint16_t
+nix_tm_lvl2nix_tl1_root(uint32_t lvl)
+{
+ switch (lvl) {
+ case ROC_TM_LVL_ROOT:
+ return NIX_TXSCH_LVL_TL1;
+ case ROC_TM_LVL_SCH1:
+ return NIX_TXSCH_LVL_TL2;
+ case ROC_TM_LVL_SCH2:
+ return NIX_TXSCH_LVL_TL3;
+ case ROC_TM_LVL_SCH3:
+ return NIX_TXSCH_LVL_TL4;
+ case ROC_TM_LVL_SCH4:
+ return NIX_TXSCH_LVL_SMQ;
+ default:
+ return NIX_TXSCH_LVL_CNT;
+ }
+}
+
+uint16_t
+nix_tm_lvl2nix_tl2_root(uint32_t lvl)
+{
+ switch (lvl) {
+ case ROC_TM_LVL_ROOT:
+ return NIX_TXSCH_LVL_TL2;
+ case ROC_TM_LVL_SCH1:
+ return NIX_TXSCH_LVL_TL3;
+ case ROC_TM_LVL_SCH2:
+ return NIX_TXSCH_LVL_TL4;
+ case ROC_TM_LVL_SCH3:
+ return NIX_TXSCH_LVL_SMQ;
+ default:
+ return NIX_TXSCH_LVL_CNT;
+ }
+}
+
+uint16_t
+nix_tm_lvl2nix(struct nix *nix, uint32_t lvl)
+{
+ if (nix_tm_have_tl1_access(nix))
+ return nix_tm_lvl2nix_tl1_root(lvl);
+ else
+ return nix_tm_lvl2nix_tl2_root(lvl);
+}
+
+
+struct nix_tm_shaper_profile *
+nix_tm_shaper_profile_search(struct nix *nix, uint32_t id)
+{
+ struct nix_tm_shaper_profile *profile;
+
+ TAILQ_FOREACH(profile, &nix->shaper_profile_list, shaper) {
+ if (profile->id == id)
+ return profile;
+ }
+ return NULL;
+}
+
struct nix_tm_node *
nix_tm_node_search(struct nix *nix, uint32_t node_id, enum roc_nix_tm_tree tree)
{
return NULL;
}
+static uint16_t
+nix_tm_max_prio(struct nix *nix, uint16_t hw_lvl)
+{
+ if (hw_lvl >= NIX_TXSCH_LVL_CNT)
+ return 0;
+
+ /* MDQ does not support SP */
+ if (hw_lvl == NIX_TXSCH_LVL_MDQ)
+ return 0;
+
+ /* PF's TL1 with VF's enabled does not support SP */
+ if (hw_lvl == NIX_TXSCH_LVL_TL1 && (!nix_tm_have_tl1_access(nix) ||
+ (nix->tm_flags & NIX_TM_TL1_NO_SP)))
+ return 0;
+
+ return NIX_TM_TLX_SP_PRIO_MAX - 1;
+}
+
+int
+nix_tm_validate_prio(struct nix *nix, uint32_t lvl, uint32_t parent_id,
+ uint32_t priority, enum roc_nix_tm_tree tree)
+{
+ uint8_t priorities[NIX_TM_TLX_SP_PRIO_MAX];
+ struct nix_tm_node_list *list;
+ struct nix_tm_node *node;
+ uint32_t rr_num = 0;
+ int i;
+
+ list = nix_tm_node_list(nix, tree);
+ /* Validate priority against max */
+ if (priority > nix_tm_max_prio(nix, nix_tm_lvl2nix(nix, lvl - 1)))
+ return NIX_ERR_TM_PRIO_EXCEEDED;
+
+ if (parent_id == ROC_NIX_TM_NODE_ID_INVALID)
+ return 0;
+
+ memset(priorities, 0, sizeof(priorities));
+ priorities[priority] = 1;
+
+ TAILQ_FOREACH(node, list, node) {
+ if (!node->parent)
+ continue;
+
+ if (node->parent->id != parent_id)
+ continue;
+
+ priorities[node->priority]++;
+ }
+
+ for (i = 0; i < NIX_TM_TLX_SP_PRIO_MAX; i++)
+ if (priorities[i] > 1)
+ rr_num++;
+
+ /* At max, one rr groups per parent */
+ if (rr_num > 1)
+ return NIX_ERR_TM_MULTIPLE_RR_GROUPS;
+
+ /* Check for previous priority to avoid holes in priorities */
+ if (priority && !priorities[priority - 1])
+ return NIX_ERR_TM_PRIO_ORDER;
+
+ return 0;
+}
+
uint8_t
nix_tm_sw_xoff_prep(struct nix_tm_node *node, bool enable,
volatile uint64_t *reg, volatile uint64_t *regval)
return k;
}
+
+uint16_t
+nix_tm_resource_avail(struct nix *nix, uint8_t hw_lvl, bool contig)
+{
+ uint32_t pos = 0, start_pos = 0;
+ struct plt_bitmap *bmp;
+ uint16_t count = 0;
+ uint64_t slab = 0;
+
+ bmp = contig ? nix->schq_contig_bmp[hw_lvl] : nix->schq_bmp[hw_lvl];
+ plt_bitmap_scan_init(bmp);
+
+ if (!plt_bitmap_scan(bmp, &pos, &slab))
+ return count;
+
+ /* Count bit set */
+ start_pos = pos;
+ do {
+ count += __builtin_popcountll(slab);
+ if (!plt_bitmap_scan(bmp, &pos, &slab))
+ break;
+ } while (pos != start_pos);
+
+ return count;
+}
+
+int
+roc_nix_tm_node_lvl(struct roc_nix *roc_nix, uint32_t node_id)
+{
+ struct nix *nix = roc_nix_to_nix_priv(roc_nix);
+ struct nix_tm_node *node;
+
+ node = nix_tm_node_search(nix, node_id, ROC_NIX_TM_USER);
+ if (!node)
+ return NIX_ERR_TM_INVALID_NODE;
+
+ return node->lvl;
+}
+
+struct roc_nix_tm_node *
+roc_nix_tm_node_get(struct roc_nix *roc_nix, uint32_t node_id)
+{
+ struct nix *nix = roc_nix_to_nix_priv(roc_nix);
+ struct nix_tm_node *node;
+
+ node = nix_tm_node_search(nix, node_id, ROC_NIX_TM_USER);
+ return (struct roc_nix_tm_node *)node;
+}
+
+struct roc_nix_tm_node *
+roc_nix_tm_node_next(struct roc_nix *roc_nix, struct roc_nix_tm_node *__prev)
+{
+ struct nix_tm_node *prev = (struct nix_tm_node *)__prev;
+ struct nix *nix = roc_nix_to_nix_priv(roc_nix);
+ struct nix_tm_node_list *list;
+
+ list = nix_tm_node_list(nix, ROC_NIX_TM_USER);
+
+ /* HEAD of the list */
+ if (!prev)
+ return (struct roc_nix_tm_node *)TAILQ_FIRST(list);
+
+ /* Next entry */
+ if (prev->tree != ROC_NIX_TM_USER)
+ return NULL;
+
+ return (struct roc_nix_tm_node *)TAILQ_NEXT(prev, node);
+}
+
+struct nix_tm_node *
+nix_tm_node_alloc(void)
+{
+ struct nix_tm_node *node;
+
+ node = plt_zmalloc(sizeof(struct nix_tm_node), 0);
+ if (!node)
+ return NULL;
+
+ node->free_fn = plt_free;
+ return node;
+}
+
+void
+nix_tm_node_free(struct nix_tm_node *node)
+{
+ if (!node || node->free_fn == NULL)
+ return;
+
+ (node->free_fn)(node);
+}
roc_nix_xstats_names_get;
roc_nix_switch_hdr_set;
roc_nix_eeprom_info_get;
+ roc_nix_tm_node_add;
+ roc_nix_tm_node_delete;
+ roc_nix_tm_node_get;
+ roc_nix_tm_node_lvl;
+ roc_nix_tm_node_name_get;
+ roc_nix_tm_node_next;
+ roc_nix_tm_node_pkt_mode_update;
roc_nix_tm_sq_aura_fc;
roc_nix_tm_sq_flush_spin;
roc_nix_unregister_cq_irqs;