From 5a960e265d74b3d4b8f9cafb2812687ce534c33f Mon Sep 17 00:00:00 2001 From: Nithin Dabilpuram Date: Tue, 6 Apr 2021 20:11:29 +0530 Subject: [PATCH] common/cnxk: support NIX TM internal hierarchy Add support to create internal TM default hierarchy and ratelimit hierarchy and API to ratelimit SQ to a given rate. This will be used by cnxk ethdev driver's tx queue ratelimit op. Signed-off-by: Nithin Dabilpuram --- drivers/common/cnxk/roc_nix.h | 7 ++ drivers/common/cnxk/roc_nix_priv.h | 2 + drivers/common/cnxk/roc_nix_tm.c | 156 +++++++++++++++++++++++++++ drivers/common/cnxk/roc_nix_tm_ops.c | 141 ++++++++++++++++++++++++ drivers/common/cnxk/version.map | 3 + 5 files changed, 309 insertions(+) diff --git a/drivers/common/cnxk/roc_nix.h b/drivers/common/cnxk/roc_nix.h index 7bf3435a2d..8992ad3026 100644 --- a/drivers/common/cnxk/roc_nix.h +++ b/drivers/common/cnxk/roc_nix.h @@ -330,6 +330,8 @@ enum roc_tm_node_level { /* * TM runtime hierarchy init API. */ +int __roc_api roc_nix_tm_init(struct roc_nix *roc_nix); +void __roc_api roc_nix_tm_fini(struct roc_nix *roc_nix); 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); @@ -391,6 +393,11 @@ roc_nix_tm_shaper_profile_get(struct roc_nix *roc_nix, uint32_t profile_id); struct roc_nix_tm_shaper_profile *__roc_api roc_nix_tm_shaper_profile_next( struct roc_nix *roc_nix, struct roc_nix_tm_shaper_profile *__prev); +/* + * TM ratelimit tree API. + */ +int __roc_api roc_nix_tm_rlimit_sq(struct roc_nix *roc_nix, uint16_t qid, + uint64_t rate); /* * TM hierarchy enable/disable API. */ diff --git a/drivers/common/cnxk/roc_nix_priv.h b/drivers/common/cnxk/roc_nix_priv.h index 5d54bd297a..d9c32df442 100644 --- a/drivers/common/cnxk/roc_nix_priv.h +++ b/drivers/common/cnxk/roc_nix_priv.h @@ -326,6 +326,7 @@ int nix_tm_leaf_data_get(struct nix *nix, uint16_t sq, uint32_t *rr_quantum, 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_prepare_default_tree(struct roc_nix *roc_nix); 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); @@ -344,6 +345,7 @@ int nix_tm_txsch_reg_config(struct nix *nix, enum roc_nix_tm_tree tree); int nix_tm_update_parent_info(struct nix *nix, enum roc_nix_tm_tree tree); int nix_tm_sq_sched_conf(struct nix *nix, struct nix_tm_node *node, bool rr_quantum_only); +int nix_tm_prepare_rate_limited_tree(struct roc_nix *roc_nix); /* * TM priv utils. diff --git a/drivers/common/cnxk/roc_nix_tm.c b/drivers/common/cnxk/roc_nix_tm.c index 762c85abd8..9b328c9669 100644 --- a/drivers/common/cnxk/roc_nix_tm.c +++ b/drivers/common/cnxk/roc_nix_tm.c @@ -1088,6 +1088,162 @@ alloc_err: return rc; } +int +nix_tm_prepare_default_tree(struct roc_nix *roc_nix) +{ + struct nix *nix = roc_nix_to_nix_priv(roc_nix); + uint32_t nonleaf_id = nix->nb_tx_queues; + struct nix_tm_node *node = NULL; + uint8_t leaf_lvl, lvl, lvl_end; + uint32_t parent, i; + int rc = 0; + + /* Add ROOT, SCH1, SCH2, SCH3, [SCH4] nodes */ + parent = ROC_NIX_TM_NODE_ID_INVALID; + /* With TL1 access we have an extra level */ + lvl_end = (nix_tm_have_tl1_access(nix) ? ROC_TM_LVL_SCH4 : + ROC_TM_LVL_SCH3); + + for (lvl = ROC_TM_LVL_ROOT; lvl <= lvl_end; lvl++) { + rc = -ENOMEM; + node = nix_tm_node_alloc(); + if (!node) + goto error; + + node->id = nonleaf_id; + node->parent_id = parent; + node->priority = 0; + node->weight = NIX_TM_DFLT_RR_WT; + node->shaper_profile_id = ROC_NIX_TM_SHAPER_PROFILE_NONE; + node->lvl = lvl; + node->tree = ROC_NIX_TM_DEFAULT; + + rc = nix_tm_node_add(roc_nix, node); + if (rc) + goto error; + parent = nonleaf_id; + nonleaf_id++; + } + + parent = nonleaf_id - 1; + leaf_lvl = (nix_tm_have_tl1_access(nix) ? ROC_TM_LVL_QUEUE : + ROC_TM_LVL_SCH4); + + /* Add leaf nodes */ + for (i = 0; i < nix->nb_tx_queues; i++) { + rc = -ENOMEM; + node = nix_tm_node_alloc(); + if (!node) + goto error; + + node->id = i; + node->parent_id = parent; + node->priority = 0; + node->weight = NIX_TM_DFLT_RR_WT; + node->shaper_profile_id = ROC_NIX_TM_SHAPER_PROFILE_NONE; + node->lvl = leaf_lvl; + node->tree = ROC_NIX_TM_DEFAULT; + + rc = nix_tm_node_add(roc_nix, node); + if (rc) + goto error; + } + + return 0; +error: + nix_tm_node_free(node); + return rc; +} + +int +nix_tm_prepare_rate_limited_tree(struct roc_nix *roc_nix) +{ + struct nix *nix = roc_nix_to_nix_priv(roc_nix); + uint32_t nonleaf_id = nix->nb_tx_queues; + struct nix_tm_node *node = NULL; + uint8_t leaf_lvl, lvl, lvl_end; + uint32_t parent, i; + int rc = 0; + + /* Add ROOT, SCH1, SCH2 nodes */ + parent = ROC_NIX_TM_NODE_ID_INVALID; + lvl_end = (nix_tm_have_tl1_access(nix) ? ROC_TM_LVL_SCH3 : + ROC_TM_LVL_SCH2); + + for (lvl = ROC_TM_LVL_ROOT; lvl <= lvl_end; lvl++) { + rc = -ENOMEM; + node = nix_tm_node_alloc(); + if (!node) + goto error; + + node->id = nonleaf_id; + node->parent_id = parent; + node->priority = 0; + node->weight = NIX_TM_DFLT_RR_WT; + node->shaper_profile_id = ROC_NIX_TM_SHAPER_PROFILE_NONE; + node->lvl = lvl; + node->tree = ROC_NIX_TM_RLIMIT; + + rc = nix_tm_node_add(roc_nix, node); + if (rc) + goto error; + parent = nonleaf_id; + nonleaf_id++; + } + + /* SMQ is mapped to SCH4 when we have TL1 access and SCH3 otherwise */ + lvl = (nix_tm_have_tl1_access(nix) ? ROC_TM_LVL_SCH4 : ROC_TM_LVL_SCH3); + + /* Add per queue SMQ nodes i.e SCH4 / SCH3 */ + for (i = 0; i < nix->nb_tx_queues; i++) { + rc = -ENOMEM; + node = nix_tm_node_alloc(); + if (!node) + goto error; + + node->id = nonleaf_id + i; + node->parent_id = parent; + node->priority = 0; + node->weight = NIX_TM_DFLT_RR_WT; + node->shaper_profile_id = ROC_NIX_TM_SHAPER_PROFILE_NONE; + node->lvl = lvl; + node->tree = ROC_NIX_TM_RLIMIT; + + rc = nix_tm_node_add(roc_nix, node); + if (rc) + goto error; + } + + parent = nonleaf_id; + leaf_lvl = (nix_tm_have_tl1_access(nix) ? ROC_TM_LVL_QUEUE : + ROC_TM_LVL_SCH4); + + /* Add leaf nodes */ + for (i = 0; i < nix->nb_tx_queues; i++) { + rc = -ENOMEM; + node = nix_tm_node_alloc(); + if (!node) + goto error; + + node->id = i; + node->parent_id = parent; + node->priority = 0; + node->weight = NIX_TM_DFLT_RR_WT; + node->shaper_profile_id = ROC_NIX_TM_SHAPER_PROFILE_NONE; + node->lvl = leaf_lvl; + node->tree = ROC_NIX_TM_RLIMIT; + + rc = nix_tm_node_add(roc_nix, node); + if (rc) + goto error; + } + + return 0; +error: + nix_tm_node_free(node); + return rc; +} + int nix_tm_free_resources(struct roc_nix *roc_nix, uint32_t tree_mask, bool hw_only) { diff --git a/drivers/common/cnxk/roc_nix_tm_ops.c b/drivers/common/cnxk/roc_nix_tm_ops.c index 6bb07668f9..d13cc8a42c 100644 --- a/drivers/common/cnxk/roc_nix_tm_ops.c +++ b/drivers/common/cnxk/roc_nix_tm_ops.c @@ -543,3 +543,144 @@ skip_sq_update: nix->tm_flags |= NIX_TM_HIERARCHY_ENA; return 0; } + +int +roc_nix_tm_init(struct roc_nix *roc_nix) +{ + struct nix *nix = roc_nix_to_nix_priv(roc_nix); + uint32_t tree_mask; + int rc; + + if (nix->tm_flags & NIX_TM_HIERARCHY_ENA) { + plt_err("Cannot init while existing hierarchy is enabled"); + return -EBUSY; + } + + /* Free up all user resources already held */ + tree_mask = NIX_TM_TREE_MASK_ALL; + rc = nix_tm_free_resources(roc_nix, tree_mask, false); + if (rc) { + plt_err("Failed to freeup all nodes and resources, rc=%d", rc); + return rc; + } + + /* Prepare default tree */ + rc = nix_tm_prepare_default_tree(roc_nix); + if (rc) { + plt_err("failed to prepare default tm tree, rc=%d", rc); + return rc; + } + + /* Prepare rlimit tree */ + rc = nix_tm_prepare_rate_limited_tree(roc_nix); + if (rc) { + plt_err("failed to prepare rlimit tm tree, rc=%d", rc); + return rc; + } + + return rc; +} + +int +roc_nix_tm_rlimit_sq(struct roc_nix *roc_nix, uint16_t qid, uint64_t rate) +{ + struct nix *nix = roc_nix_to_nix_priv(roc_nix); + struct nix_tm_shaper_profile profile; + struct mbox *mbox = (&nix->dev)->mbox; + struct nix_tm_node *node, *parent; + + volatile uint64_t *reg, *regval; + struct nix_txschq_config *req; + uint16_t flags; + uint8_t k = 0; + int rc; + + if (nix->tm_tree != ROC_NIX_TM_RLIMIT || + !(nix->tm_flags & NIX_TM_HIERARCHY_ENA)) + return NIX_ERR_TM_INVALID_TREE; + + node = nix_tm_node_search(nix, qid, ROC_NIX_TM_RLIMIT); + + /* check if we found a valid leaf node */ + if (!node || !nix_tm_is_leaf(nix, node->lvl) || !node->parent || + node->parent->hw_id == NIX_TM_HW_ID_INVALID) + return NIX_ERR_TM_INVALID_NODE; + + parent = node->parent; + flags = parent->flags; + + req = mbox_alloc_msg_nix_txschq_cfg(mbox); + req->lvl = NIX_TXSCH_LVL_MDQ; + reg = req->reg; + regval = req->regval; + + if (rate == 0) { + k += nix_tm_sw_xoff_prep(parent, true, ®[k], ®val[k]); + flags &= ~NIX_TM_NODE_ENABLED; + goto exit; + } + + if (!(flags & NIX_TM_NODE_ENABLED)) { + k += nix_tm_sw_xoff_prep(parent, false, ®[k], ®val[k]); + flags |= NIX_TM_NODE_ENABLED; + } + + /* Use only PIR for rate limit */ + memset(&profile, 0, sizeof(profile)); + profile.peak.rate = rate; + /* Minimum burst of ~4us Bytes of Tx */ + profile.peak.size = PLT_MAX((uint64_t)roc_nix_max_pkt_len(roc_nix), + (4ul * rate) / ((uint64_t)1E6 * 8)); + if (!nix->tm_rate_min || nix->tm_rate_min > rate) + nix->tm_rate_min = rate; + + k += nix_tm_shaper_reg_prep(parent, &profile, ®[k], ®val[k]); +exit: + req->num_regs = k; + rc = mbox_process(mbox); + if (rc) + return rc; + + parent->flags = flags; + return 0; +} + +void +roc_nix_tm_fini(struct roc_nix *roc_nix) +{ + struct nix *nix = roc_nix_to_nix_priv(roc_nix); + struct mbox *mbox = (&nix->dev)->mbox; + struct nix_txsch_free_req *req; + uint32_t tree_mask; + uint8_t hw_lvl; + int rc; + + /* Xmit is assumed to be disabled */ + /* Free up resources already held */ + tree_mask = NIX_TM_TREE_MASK_ALL; + rc = nix_tm_free_resources(roc_nix, tree_mask, false); + if (rc) + plt_err("Failed to freeup existing nodes or rsrcs, rc=%d", rc); + + /* Free all other hw resources */ + req = mbox_alloc_msg_nix_txsch_free(mbox); + if (req == NULL) + return; + + req->flags = TXSCHQ_FREE_ALL; + rc = mbox_process(mbox); + if (rc) + plt_err("Failed to freeup all res, rc=%d", rc); + + for (hw_lvl = 0; hw_lvl < NIX_TXSCH_LVL_CNT; hw_lvl++) { + plt_bitmap_reset(nix->schq_bmp[hw_lvl]); + plt_bitmap_reset(nix->schq_contig_bmp[hw_lvl]); + nix->contig_rsvd[hw_lvl] = 0; + nix->discontig_rsvd[hw_lvl] = 0; + } + + /* Clear shaper profiles */ + nix_tm_clear_shaper_profiles(nix); + nix->tm_tree = 0; + nix->tm_flags &= ~NIX_TM_HIERARCHY_ENA; +} diff --git a/drivers/common/cnxk/version.map b/drivers/common/cnxk/version.map index 2a1cc98148..cb3ddb28a3 100644 --- a/drivers/common/cnxk/version.map +++ b/drivers/common/cnxk/version.map @@ -104,9 +104,11 @@ INTERNAL { roc_nix_xstats_names_get; roc_nix_switch_hdr_set; roc_nix_eeprom_info_get; + roc_nix_tm_fini; roc_nix_tm_free_resources; roc_nix_tm_hierarchy_disable; roc_nix_tm_hierarchy_enable; + roc_nix_tm_init; roc_nix_tm_node_add; roc_nix_tm_node_delete; roc_nix_tm_node_get; @@ -114,6 +116,7 @@ INTERNAL { roc_nix_tm_node_name_get; roc_nix_tm_node_next; roc_nix_tm_node_pkt_mode_update; + roc_nix_tm_rlimit_sq; roc_nix_tm_shaper_profile_add; roc_nix_tm_shaper_profile_delete; roc_nix_tm_shaper_profile_get; -- 2.20.1