1134d75881
Add FC check in vector event Tx path, the check needs to be performed after head wait right before LMTST is issued. Since, SQB pool FC updates are delayed w.r.t the actual utilization of pool add sufficient slack to avoid overflow. Added a new device argument to override the default SQB slack configured, can be used as follows: -a 0002:02:00.0,sqb_slack=32 Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
1145 lines
28 KiB
C
1145 lines
28 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(C) 2021 Marvell.
|
|
*/
|
|
|
|
#include "roc_api.h"
|
|
#include "roc_priv.h"
|
|
|
|
int
|
|
roc_nix_tm_sq_aura_fc(struct roc_nix_sq *sq, bool enable)
|
|
{
|
|
struct npa_aq_enq_req *req;
|
|
struct npa_aq_enq_rsp *rsp;
|
|
uint64_t aura_handle;
|
|
struct npa_lf *lf;
|
|
struct mbox *mbox;
|
|
int rc = -ENOSPC;
|
|
|
|
plt_tm_dbg("Setting SQ %u SQB aura FC to %s", sq->qid,
|
|
enable ? "enable" : "disable");
|
|
|
|
lf = idev_npa_obj_get();
|
|
if (!lf)
|
|
return NPA_ERR_DEVICE_NOT_BOUNDED;
|
|
|
|
mbox = lf->mbox;
|
|
/* Set/clear sqb aura fc_ena */
|
|
aura_handle = sq->aura_handle;
|
|
req = mbox_alloc_msg_npa_aq_enq(mbox);
|
|
if (req == NULL)
|
|
return rc;
|
|
|
|
req->aura_id = roc_npa_aura_handle_to_aura(aura_handle);
|
|
req->ctype = NPA_AQ_CTYPE_AURA;
|
|
req->op = NPA_AQ_INSTOP_WRITE;
|
|
/* Below is not needed for aura writes but AF driver needs it */
|
|
/* AF will translate to associated poolctx */
|
|
req->aura.pool_addr = req->aura_id;
|
|
|
|
req->aura.fc_ena = enable;
|
|
req->aura_mask.fc_ena = 1;
|
|
if (roc_model_is_cn9k() || roc_errata_npa_has_no_fc_stype_ststp()) {
|
|
req->aura.fc_stype = 0x0; /* STF */
|
|
req->aura_mask.fc_stype = 0x0; /* STF */
|
|
} else {
|
|
req->aura.fc_stype = 0x3; /* STSTP */
|
|
req->aura_mask.fc_stype = 0x3; /* STSTP */
|
|
}
|
|
|
|
rc = mbox_process(mbox);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Read back npa aura ctx */
|
|
req = mbox_alloc_msg_npa_aq_enq(mbox);
|
|
if (req == NULL)
|
|
return -ENOSPC;
|
|
|
|
req->aura_id = roc_npa_aura_handle_to_aura(aura_handle);
|
|
req->ctype = NPA_AQ_CTYPE_AURA;
|
|
req->op = NPA_AQ_INSTOP_READ;
|
|
|
|
rc = mbox_process_msg(mbox, (void *)&rsp);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Init when enabled as there might be no triggers */
|
|
if (enable)
|
|
*(volatile uint64_t *)sq->fc = rsp->aura.count;
|
|
else
|
|
*(volatile uint64_t *)sq->fc = sq->aura_sqb_bufs;
|
|
/* Sync write barrier */
|
|
plt_wmb();
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_free_resources(struct roc_nix *roc_nix, bool hw_only)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
|
|
if (nix->tm_flags & NIX_TM_HIERARCHY_ENA)
|
|
return -EBUSY;
|
|
|
|
return nix_tm_free_resources(roc_nix, BIT(ROC_NIX_TM_USER), hw_only);
|
|
}
|
|
|
|
static int
|
|
nix_tm_adjust_shaper_pps_rate(struct nix_tm_shaper_profile *profile)
|
|
{
|
|
uint64_t min_rate = profile->commit.rate;
|
|
|
|
if (!profile->pkt_mode)
|
|
return 0;
|
|
|
|
profile->pkt_mode_adj = 1;
|
|
|
|
if (profile->commit.rate &&
|
|
(profile->commit.rate < NIX_TM_MIN_SHAPER_PPS_RATE ||
|
|
profile->commit.rate > NIX_TM_MAX_SHAPER_PPS_RATE))
|
|
return NIX_ERR_TM_INVALID_COMMIT_RATE;
|
|
|
|
if (profile->peak.rate &&
|
|
(profile->peak.rate < NIX_TM_MIN_SHAPER_PPS_RATE ||
|
|
profile->peak.rate > NIX_TM_MAX_SHAPER_PPS_RATE))
|
|
return NIX_ERR_TM_INVALID_PEAK_RATE;
|
|
|
|
if (profile->peak.rate && min_rate > profile->peak.rate)
|
|
min_rate = profile->peak.rate;
|
|
|
|
/* Each packet accumulate single count, whereas HW
|
|
* considers each unit as Byte, so we need convert
|
|
* user pps to bps
|
|
*/
|
|
profile->commit.rate = profile->commit.rate * 8;
|
|
profile->peak.rate = profile->peak.rate * 8;
|
|
min_rate = min_rate * 8;
|
|
|
|
if (min_rate && (min_rate < NIX_TM_MIN_SHAPER_RATE)) {
|
|
int adjust = NIX_TM_MIN_SHAPER_RATE / min_rate;
|
|
|
|
if (adjust > NIX_TM_LENGTH_ADJUST_MAX)
|
|
return NIX_ERR_TM_SHAPER_PKT_LEN_ADJUST;
|
|
|
|
profile->pkt_mode_adj += adjust;
|
|
profile->commit.rate += (adjust * profile->commit.rate);
|
|
profile->peak.rate += (adjust * profile->peak.rate);
|
|
/* Number of tokens freed after scheduling was proportional
|
|
* to adjust value
|
|
*/
|
|
profile->commit.size *= adjust;
|
|
profile->peak.size *= adjust;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nix_tm_shaper_profile_add(struct roc_nix *roc_nix,
|
|
struct nix_tm_shaper_profile *profile, int skip_ins)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
uint64_t commit_rate, commit_sz;
|
|
uint64_t min_burst, max_burst;
|
|
uint64_t peak_rate, peak_sz;
|
|
uint32_t id;
|
|
int rc;
|
|
|
|
id = profile->id;
|
|
rc = nix_tm_adjust_shaper_pps_rate(profile);
|
|
if (rc)
|
|
return rc;
|
|
|
|
commit_rate = profile->commit.rate;
|
|
commit_sz = profile->commit.size;
|
|
peak_rate = profile->peak.rate;
|
|
peak_sz = profile->peak.size;
|
|
|
|
min_burst = NIX_TM_MIN_SHAPER_BURST;
|
|
max_burst = roc_nix_tm_max_shaper_burst_get();
|
|
|
|
if (nix_tm_shaper_profile_search(nix, id) && !skip_ins)
|
|
return NIX_ERR_TM_SHAPER_PROFILE_EXISTS;
|
|
|
|
if (profile->pkt_len_adj < NIX_TM_LENGTH_ADJUST_MIN ||
|
|
profile->pkt_len_adj > NIX_TM_LENGTH_ADJUST_MAX)
|
|
return NIX_ERR_TM_SHAPER_PKT_LEN_ADJUST;
|
|
|
|
/* We cannot support both pkt length adjust and pkt mode */
|
|
if (profile->pkt_mode && profile->pkt_len_adj)
|
|
return NIX_ERR_TM_SHAPER_PKT_LEN_ADJUST;
|
|
|
|
/* commit rate and burst size can be enabled/disabled */
|
|
if (commit_rate || commit_sz) {
|
|
if (commit_sz < min_burst || commit_sz > max_burst)
|
|
return NIX_ERR_TM_INVALID_COMMIT_SZ;
|
|
else if (!nix_tm_shaper_rate_conv(commit_rate, NULL, NULL, NULL,
|
|
profile->accuracy))
|
|
return NIX_ERR_TM_INVALID_COMMIT_RATE;
|
|
}
|
|
|
|
/* Peak rate and burst size can be enabled/disabled */
|
|
if (peak_sz || peak_rate) {
|
|
if (peak_sz < min_burst || peak_sz > max_burst)
|
|
return NIX_ERR_TM_INVALID_PEAK_SZ;
|
|
else if (!nix_tm_shaper_rate_conv(peak_rate, NULL, NULL, NULL,
|
|
profile->accuracy))
|
|
return NIX_ERR_TM_INVALID_PEAK_RATE;
|
|
}
|
|
|
|
/* If PIR and CIR are requested, PIR should always be larger than CIR */
|
|
if (peak_rate && commit_rate && (commit_rate > peak_rate))
|
|
return NIX_ERR_TM_INVALID_PEAK_RATE;
|
|
|
|
if (!skip_ins)
|
|
TAILQ_INSERT_TAIL(&nix->shaper_profile_list, profile, shaper);
|
|
|
|
plt_tm_dbg("Added TM shaper profile %u, "
|
|
" pir %" PRIu64 " , pbs %" PRIu64 ", cir %" PRIu64
|
|
", cbs %" PRIu64 " , adj %u, pkt_mode %u",
|
|
id, profile->peak.rate, profile->peak.size,
|
|
profile->commit.rate, profile->commit.size,
|
|
profile->pkt_len_adj, profile->pkt_mode);
|
|
|
|
/* Always use PIR for single rate shaping */
|
|
if (!peak_rate && commit_rate) {
|
|
profile->peak.rate = profile->commit.rate;
|
|
profile->peak.size = profile->commit.size;
|
|
profile->commit.rate = 0;
|
|
profile->commit.size = 0;
|
|
}
|
|
|
|
/* update min rate */
|
|
nix->tm_rate_min = nix_tm_shaper_profile_rate_min(nix);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_shaper_profile_add(struct roc_nix *roc_nix,
|
|
struct roc_nix_tm_shaper_profile *roc_profile)
|
|
{
|
|
struct nix_tm_shaper_profile *profile;
|
|
|
|
profile = (struct nix_tm_shaper_profile *)roc_profile->reserved;
|
|
|
|
profile->ref_cnt = 0;
|
|
profile->id = roc_profile->id;
|
|
profile->commit.rate = roc_profile->commit_rate;
|
|
profile->peak.rate = roc_profile->peak_rate;
|
|
profile->commit.size = roc_profile->commit_sz;
|
|
profile->peak.size = roc_profile->peak_sz;
|
|
profile->pkt_len_adj = roc_profile->pkt_len_adj;
|
|
profile->pkt_mode = roc_profile->pkt_mode;
|
|
profile->free_fn = roc_profile->free_fn;
|
|
profile->accuracy = roc_profile->accuracy;
|
|
|
|
return nix_tm_shaper_profile_add(roc_nix, profile, 0);
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_shaper_profile_update(struct roc_nix *roc_nix,
|
|
struct roc_nix_tm_shaper_profile *roc_profile)
|
|
{
|
|
struct nix_tm_shaper_profile *profile;
|
|
|
|
profile = (struct nix_tm_shaper_profile *)roc_profile->reserved;
|
|
|
|
profile->commit.rate = roc_profile->commit_rate;
|
|
profile->peak.rate = roc_profile->peak_rate;
|
|
profile->commit.size = roc_profile->commit_sz;
|
|
profile->peak.size = roc_profile->peak_sz;
|
|
profile->pkt_len_adj = roc_profile->pkt_len_adj;
|
|
profile->accuracy = roc_profile->accuracy;
|
|
|
|
return nix_tm_shaper_profile_add(roc_nix, profile, 1);
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_shaper_profile_delete(struct roc_nix *roc_nix, uint32_t id)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
struct nix_tm_shaper_profile *profile;
|
|
|
|
profile = nix_tm_shaper_profile_search(nix, id);
|
|
if (!profile)
|
|
return NIX_ERR_TM_INVALID_SHAPER_PROFILE;
|
|
|
|
if (profile->ref_cnt)
|
|
return NIX_ERR_TM_SHAPER_PROFILE_IN_USE;
|
|
|
|
plt_tm_dbg("Removing TM shaper profile %u", id);
|
|
TAILQ_REMOVE(&nix->shaper_profile_list, profile, shaper);
|
|
nix_tm_shaper_profile_free(profile);
|
|
|
|
/* update min rate */
|
|
nix->tm_rate_min = nix_tm_shaper_profile_rate_min(nix);
|
|
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;
|
|
node->rel_chan = NIX_TM_CHAN_INVALID;
|
|
|
|
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);
|
|
}
|
|
|
|
int
|
|
roc_nix_smq_flush(struct roc_nix *roc_nix)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
struct nix_tm_node_list *list;
|
|
enum roc_nix_tm_tree tree;
|
|
struct nix_tm_node *node;
|
|
int rc = 0;
|
|
|
|
if (!(nix->tm_flags & NIX_TM_HIERARCHY_ENA))
|
|
return 0;
|
|
|
|
tree = nix->tm_tree;
|
|
list = nix_tm_node_list(nix, tree);
|
|
|
|
/* XOFF & Flush all SMQ's. HRM mandates
|
|
* all SQ's empty before SMQ flush is issued.
|
|
*/
|
|
TAILQ_FOREACH(node, list, node) {
|
|
if (node->hw_lvl != NIX_TXSCH_LVL_SMQ)
|
|
continue;
|
|
if (!(node->flags & NIX_TM_NODE_HWRES))
|
|
continue;
|
|
|
|
rc = nix_tm_smq_xoff(nix, node, true);
|
|
if (rc) {
|
|
plt_err("Failed to enable smq %u, rc=%d", node->hw_id,
|
|
rc);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
/* XON all SMQ's */
|
|
TAILQ_FOREACH(node, list, node) {
|
|
if (node->hw_lvl != NIX_TXSCH_LVL_SMQ)
|
|
continue;
|
|
if (!(node->flags & NIX_TM_NODE_HWRES))
|
|
continue;
|
|
|
|
rc = nix_tm_smq_xoff(nix, node, false);
|
|
if (rc) {
|
|
plt_err("Failed to enable smq %u, rc=%d", node->hw_id,
|
|
rc);
|
|
goto exit;
|
|
}
|
|
}
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_hierarchy_disable(struct roc_nix *roc_nix)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
uint16_t sqb_cnt, head_off, tail_off;
|
|
uint16_t sq_cnt = nix->nb_tx_queues;
|
|
struct mbox *mbox = (&nix->dev)->mbox;
|
|
struct nix_tm_node_list *list;
|
|
enum roc_nix_tm_tree tree;
|
|
struct nix_tm_node *node;
|
|
struct roc_nix_sq *sq;
|
|
uint64_t wdata, val;
|
|
uintptr_t regaddr;
|
|
int rc = -1, i;
|
|
|
|
if (!(nix->tm_flags & NIX_TM_HIERARCHY_ENA))
|
|
return 0;
|
|
|
|
plt_tm_dbg("Disabling hierarchy on %s", nix->pci_dev->name);
|
|
|
|
tree = nix->tm_tree;
|
|
list = nix_tm_node_list(nix, tree);
|
|
|
|
/* Enable CGX RXTX to drain pkts */
|
|
if (!roc_nix->io_enabled) {
|
|
/* Though it enables both RX MCAM Entries and CGX Link
|
|
* we assume all the rx queues are stopped way back.
|
|
*/
|
|
mbox_alloc_msg_nix_lf_start_rx(mbox);
|
|
rc = mbox_process(mbox);
|
|
if (rc) {
|
|
plt_err("cgx start failed, rc=%d", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* XON all SMQ's */
|
|
TAILQ_FOREACH(node, list, node) {
|
|
if (node->hw_lvl != NIX_TXSCH_LVL_SMQ)
|
|
continue;
|
|
if (!(node->flags & NIX_TM_NODE_HWRES))
|
|
continue;
|
|
|
|
rc = nix_tm_smq_xoff(nix, node, false);
|
|
if (rc) {
|
|
plt_err("Failed to enable smq %u, rc=%d", node->hw_id,
|
|
rc);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Disable backpressure, it will be enabled back if needed on
|
|
* hierarchy enable
|
|
*/
|
|
for (i = 0; i < sq_cnt; i++) {
|
|
sq = nix->sqs[i];
|
|
if (!sq)
|
|
continue;
|
|
|
|
rc = nix_tm_bp_config_set(roc_nix, sq->qid, 0, false, false);
|
|
if (rc && rc != -ENOENT) {
|
|
plt_err("Failed to disable backpressure, rc=%d", rc);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Flush all tx queues */
|
|
for (i = 0; i < sq_cnt; i++) {
|
|
sq = nix->sqs[i];
|
|
if (!sq)
|
|
continue;
|
|
|
|
rc = roc_nix_tm_sq_aura_fc(sq, false);
|
|
if (rc) {
|
|
plt_err("Failed to disable sqb aura fc, rc=%d", rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Wait for sq entries to be flushed */
|
|
rc = roc_nix_tm_sq_flush_spin(sq);
|
|
if (rc) {
|
|
plt_err("Failed to drain sq, rc=%d\n", rc);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* XOFF & Flush all SMQ's. HRM mandates
|
|
* all SQ's empty before SMQ flush is issued.
|
|
*/
|
|
TAILQ_FOREACH(node, list, node) {
|
|
if (node->hw_lvl != NIX_TXSCH_LVL_SMQ)
|
|
continue;
|
|
if (!(node->flags & NIX_TM_NODE_HWRES))
|
|
continue;
|
|
|
|
rc = nix_tm_smq_xoff(nix, node, true);
|
|
if (rc) {
|
|
plt_err("Failed to enable smq %u, rc=%d", node->hw_id,
|
|
rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
node->flags &= ~NIX_TM_NODE_ENABLED;
|
|
}
|
|
|
|
/* Verify sanity of all tx queues */
|
|
for (i = 0; i < sq_cnt; i++) {
|
|
sq = nix->sqs[i];
|
|
if (!sq)
|
|
continue;
|
|
|
|
wdata = ((uint64_t)sq->qid << 32);
|
|
regaddr = nix->base + NIX_LF_SQ_OP_STATUS;
|
|
val = roc_atomic64_add_nosync(wdata, (int64_t *)regaddr);
|
|
|
|
sqb_cnt = val & 0xFFFF;
|
|
head_off = (val >> 20) & 0x3F;
|
|
tail_off = (val >> 28) & 0x3F;
|
|
|
|
if (sqb_cnt > 1 || head_off != tail_off ||
|
|
(*(uint64_t *)sq->fc != sq->aura_sqb_bufs))
|
|
plt_err("Failed to gracefully flush sq %u", sq->qid);
|
|
}
|
|
|
|
nix->tm_flags &= ~NIX_TM_HIERARCHY_ENA;
|
|
cleanup:
|
|
/* Restore cgx state */
|
|
if (!roc_nix->io_enabled) {
|
|
mbox_alloc_msg_nix_lf_stop_rx(mbox);
|
|
rc |= mbox_process(mbox);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_hierarchy_enable(struct roc_nix *roc_nix, enum roc_nix_tm_tree tree,
|
|
bool xmit_enable)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
struct nix_tm_node_list *list;
|
|
struct nix_tm_node *node;
|
|
struct roc_nix_sq *sq;
|
|
uint32_t tree_mask;
|
|
uint16_t sq_id;
|
|
int rc;
|
|
|
|
if (tree >= ROC_NIX_TM_TREE_MAX)
|
|
return NIX_ERR_PARAM;
|
|
|
|
if (nix->tm_flags & NIX_TM_HIERARCHY_ENA) {
|
|
if (nix->tm_tree != tree)
|
|
return -EBUSY;
|
|
return 0;
|
|
}
|
|
|
|
plt_tm_dbg("Enabling hierarchy on %s, xmit_ena %u, tree %u",
|
|
nix->pci_dev->name, xmit_enable, tree);
|
|
|
|
/* Free hw resources of other trees */
|
|
tree_mask = NIX_TM_TREE_MASK_ALL;
|
|
tree_mask &= ~BIT(tree);
|
|
|
|
rc = nix_tm_free_resources(roc_nix, tree_mask, true);
|
|
if (rc) {
|
|
plt_err("failed to free resources of other trees, rc=%d", rc);
|
|
return rc;
|
|
}
|
|
|
|
/* Update active tree before starting to do anything */
|
|
nix->tm_tree = tree;
|
|
|
|
nix_tm_update_parent_info(nix, tree);
|
|
|
|
rc = nix_tm_alloc_txschq(nix, tree);
|
|
if (rc) {
|
|
plt_err("TM failed to alloc tm resources=%d", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = nix_tm_assign_resources(nix, tree);
|
|
if (rc) {
|
|
plt_err("TM failed to assign tm resources=%d", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = nix_tm_txsch_reg_config(nix, tree);
|
|
if (rc) {
|
|
plt_err("TM failed to configure sched registers=%d", rc);
|
|
return rc;
|
|
}
|
|
|
|
list = nix_tm_node_list(nix, tree);
|
|
/* Mark all non-leaf's as enabled */
|
|
TAILQ_FOREACH(node, list, node) {
|
|
if (!nix_tm_is_leaf(nix, node->lvl))
|
|
node->flags |= NIX_TM_NODE_ENABLED;
|
|
}
|
|
|
|
if (!xmit_enable)
|
|
goto skip_sq_update;
|
|
|
|
/* Update SQ Sched Data while SQ is idle */
|
|
TAILQ_FOREACH(node, list, node) {
|
|
if (!nix_tm_is_leaf(nix, node->lvl))
|
|
continue;
|
|
|
|
rc = nix_tm_sq_sched_conf(nix, node, false);
|
|
if (rc) {
|
|
plt_err("SQ %u sched update failed, rc=%d", node->id,
|
|
rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* Finally XON all SMQ's */
|
|
TAILQ_FOREACH(node, list, node) {
|
|
if (node->hw_lvl != NIX_TXSCH_LVL_SMQ)
|
|
continue;
|
|
|
|
rc = nix_tm_smq_xoff(nix, node, false);
|
|
if (rc) {
|
|
plt_err("Failed to enable smq %u, rc=%d", node->hw_id,
|
|
rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* Enable xmit as all the topology is ready */
|
|
TAILQ_FOREACH(node, list, node) {
|
|
if (!nix_tm_is_leaf(nix, node->lvl))
|
|
continue;
|
|
|
|
sq_id = node->id;
|
|
sq = nix->sqs[sq_id];
|
|
|
|
rc = roc_nix_tm_sq_aura_fc(sq, true);
|
|
if (rc) {
|
|
plt_err("TM sw xon failed on SQ %u, rc=%d", node->id,
|
|
rc);
|
|
return rc;
|
|
}
|
|
node->flags |= NIX_TM_NODE_ENABLED;
|
|
}
|
|
|
|
skip_sq_update:
|
|
nix->tm_flags |= NIX_TM_HIERARCHY_ENA;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_node_suspend_resume(struct roc_nix *roc_nix, uint32_t node_id,
|
|
bool suspend)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
struct mbox *mbox = (&nix->dev)->mbox;
|
|
struct nix_txschq_config *req;
|
|
struct nix_tm_node *node;
|
|
uint16_t flags;
|
|
int rc;
|
|
|
|
node = nix_tm_node_search(nix, node_id, ROC_NIX_TM_USER);
|
|
if (!node)
|
|
return NIX_ERR_TM_INVALID_NODE;
|
|
|
|
flags = node->flags;
|
|
flags = suspend ? (flags & ~NIX_TM_NODE_ENABLED) :
|
|
(flags | NIX_TM_NODE_ENABLED);
|
|
|
|
if (node->flags == flags)
|
|
return 0;
|
|
|
|
/* send mbox for state change */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
|
|
req->lvl = node->hw_lvl;
|
|
req->num_regs =
|
|
nix_tm_sw_xoff_prep(node, suspend, req->reg, req->regval);
|
|
rc = mbox_process(mbox);
|
|
if (!rc)
|
|
node->flags = flags;
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_prealloc_res(struct roc_nix *roc_nix, uint8_t lvl,
|
|
uint16_t discontig, uint16_t contig)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
struct mbox *mbox = (&nix->dev)->mbox;
|
|
struct nix_txsch_alloc_req *req;
|
|
struct nix_txsch_alloc_rsp *rsp;
|
|
uint8_t hw_lvl;
|
|
int rc = -ENOSPC;
|
|
|
|
hw_lvl = nix_tm_lvl2nix(nix, lvl);
|
|
if (hw_lvl == NIX_TXSCH_LVL_CNT)
|
|
return -EINVAL;
|
|
|
|
/* Preallocate contiguous */
|
|
if (nix->contig_rsvd[hw_lvl] < contig) {
|
|
req = mbox_alloc_msg_nix_txsch_alloc(mbox);
|
|
if (req == NULL)
|
|
return rc;
|
|
req->schq_contig[hw_lvl] = contig - nix->contig_rsvd[hw_lvl];
|
|
|
|
rc = mbox_process_msg(mbox, (void *)&rsp);
|
|
if (rc)
|
|
return rc;
|
|
|
|
nix_tm_copy_rsp_to_nix(nix, rsp);
|
|
}
|
|
|
|
/* Preallocate contiguous */
|
|
if (nix->discontig_rsvd[hw_lvl] < discontig) {
|
|
req = mbox_alloc_msg_nix_txsch_alloc(mbox);
|
|
if (req == NULL)
|
|
return -ENOSPC;
|
|
req->schq[hw_lvl] = discontig - nix->discontig_rsvd[hw_lvl];
|
|
|
|
rc = mbox_process_msg(mbox, (void *)&rsp);
|
|
if (rc)
|
|
return rc;
|
|
|
|
nix_tm_copy_rsp_to_nix(nix, rsp);
|
|
}
|
|
|
|
/* Save thresholds */
|
|
nix->contig_rsvd[hw_lvl] = contig;
|
|
nix->discontig_rsvd[hw_lvl] = discontig;
|
|
/* Release anything present above thresholds */
|
|
nix_tm_release_resources(nix, hw_lvl, true, true);
|
|
nix_tm_release_resources(nix, hw_lvl, false, true);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_node_shaper_update(struct roc_nix *roc_nix, uint32_t node_id,
|
|
uint32_t profile_id, bool force_update)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
struct nix_tm_shaper_profile *profile = NULL;
|
|
struct mbox *mbox = (&nix->dev)->mbox;
|
|
struct nix_txschq_config *req;
|
|
struct nix_tm_node *node;
|
|
uint8_t k;
|
|
int rc;
|
|
|
|
/* Shaper updates valid only for user nodes */
|
|
node = nix_tm_node_search(nix, node_id, ROC_NIX_TM_USER);
|
|
if (!node || nix_tm_is_leaf(nix, node->lvl))
|
|
return NIX_ERR_TM_INVALID_NODE;
|
|
|
|
if (profile_id != ROC_NIX_TM_SHAPER_PROFILE_NONE) {
|
|
profile = nix_tm_shaper_profile_search(nix, profile_id);
|
|
if (!profile)
|
|
return NIX_ERR_TM_INVALID_SHAPER_PROFILE;
|
|
}
|
|
|
|
/* Pkt mode should match existing node's pkt mode */
|
|
if (profile && profile->pkt_mode != node->pkt_mode)
|
|
return NIX_ERR_TM_PKT_MODE_MISMATCH;
|
|
|
|
if ((profile_id == node->shaper_profile_id) && !force_update) {
|
|
return 0;
|
|
} else if (profile_id != node->shaper_profile_id) {
|
|
struct nix_tm_shaper_profile *old;
|
|
|
|
/* Find old shaper profile and reduce ref count */
|
|
old = nix_tm_shaper_profile_search(nix,
|
|
node->shaper_profile_id);
|
|
if (old)
|
|
old->ref_cnt--;
|
|
|
|
if (profile)
|
|
profile->ref_cnt++;
|
|
|
|
/* Reduce older shaper ref count and increase new one */
|
|
node->shaper_profile_id = profile_id;
|
|
}
|
|
|
|
/* Nothing to do if hierarchy not yet enabled */
|
|
if (!(nix->tm_flags & NIX_TM_HIERARCHY_ENA))
|
|
return 0;
|
|
|
|
node->flags &= ~NIX_TM_NODE_ENABLED;
|
|
|
|
/* Flush the specific node with SW_XOFF */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
req->lvl = node->hw_lvl;
|
|
k = nix_tm_sw_xoff_prep(node, true, req->reg, req->regval);
|
|
req->num_regs = k;
|
|
|
|
rc = mbox_process(mbox);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Update the PIR/CIR and clear SW XOFF */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
req->lvl = node->hw_lvl;
|
|
|
|
k = nix_tm_shaper_reg_prep(node, profile, req->reg, req->regval);
|
|
|
|
k += nix_tm_sw_xoff_prep(node, false, &req->reg[k], &req->regval[k]);
|
|
|
|
req->num_regs = k;
|
|
rc = mbox_process(mbox);
|
|
if (!rc)
|
|
node->flags |= NIX_TM_NODE_ENABLED;
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_node_parent_update(struct roc_nix *roc_nix, uint32_t node_id,
|
|
uint32_t new_parent_id, uint32_t priority,
|
|
uint32_t weight)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
struct mbox *mbox = (&nix->dev)->mbox;
|
|
struct nix_tm_node *node, *sibling;
|
|
struct nix_tm_node *new_parent;
|
|
struct nix_txschq_config *req;
|
|
struct nix_tm_node_list *list;
|
|
uint8_t k;
|
|
int rc;
|
|
|
|
node = nix_tm_node_search(nix, node_id, ROC_NIX_TM_USER);
|
|
if (!node)
|
|
return NIX_ERR_TM_INVALID_NODE;
|
|
|
|
/* Parent id valid only for non root nodes */
|
|
if (node->hw_lvl != nix->tm_root_lvl) {
|
|
new_parent =
|
|
nix_tm_node_search(nix, new_parent_id, ROC_NIX_TM_USER);
|
|
if (!new_parent)
|
|
return NIX_ERR_TM_INVALID_PARENT;
|
|
|
|
/* Current support is only for dynamic weight update */
|
|
if (node->parent != new_parent || node->priority != priority)
|
|
return NIX_ERR_TM_PARENT_PRIO_UPDATE;
|
|
}
|
|
|
|
list = nix_tm_node_list(nix, ROC_NIX_TM_USER);
|
|
/* Skip if no change */
|
|
if (node->weight == weight)
|
|
return 0;
|
|
|
|
node->weight = weight;
|
|
|
|
/* Nothing to do if hierarchy not yet enabled */
|
|
if (!(nix->tm_flags & NIX_TM_HIERARCHY_ENA))
|
|
return 0;
|
|
|
|
/* For leaf nodes, SQ CTX needs update */
|
|
if (nix_tm_is_leaf(nix, node->lvl)) {
|
|
/* Update SQ quantum data on the fly */
|
|
rc = nix_tm_sq_sched_conf(nix, node, true);
|
|
if (rc)
|
|
return NIX_ERR_TM_SQ_UPDATE_FAIL;
|
|
} else {
|
|
/* XOFF Parent node */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
req->lvl = node->parent->hw_lvl;
|
|
req->num_regs = nix_tm_sw_xoff_prep(node->parent, true,
|
|
req->reg, req->regval);
|
|
rc = mbox_process(mbox);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* XOFF this node and all other siblings */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
req->lvl = node->hw_lvl;
|
|
|
|
k = 0;
|
|
TAILQ_FOREACH(sibling, list, node) {
|
|
if (sibling->parent != node->parent)
|
|
continue;
|
|
k += nix_tm_sw_xoff_prep(sibling, true, &req->reg[k],
|
|
&req->regval[k]);
|
|
}
|
|
req->num_regs = k;
|
|
rc = mbox_process(mbox);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Update new weight for current node */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
req->lvl = node->hw_lvl;
|
|
req->num_regs =
|
|
nix_tm_sched_reg_prep(nix, node, req->reg, req->regval);
|
|
rc = mbox_process(mbox);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* XON this node and all other siblings */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
req->lvl = node->hw_lvl;
|
|
|
|
k = 0;
|
|
TAILQ_FOREACH(sibling, list, node) {
|
|
if (sibling->parent != node->parent)
|
|
continue;
|
|
k += nix_tm_sw_xoff_prep(sibling, false, &req->reg[k],
|
|
&req->regval[k]);
|
|
}
|
|
req->num_regs = k;
|
|
rc = mbox_process(mbox);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* XON Parent node */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
req->lvl = node->parent->hw_lvl;
|
|
req->num_regs = nix_tm_sw_xoff_prep(node->parent, false,
|
|
req->reg, req->regval);
|
|
rc = mbox_process(mbox);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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_USER) ||
|
|
!(nix->tm_flags & NIX_TM_HIERARCHY_ENA))
|
|
return NIX_ERR_TM_INVALID_TREE;
|
|
|
|
node = nix_tm_node_search(nix, qid, nix->tm_tree);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_rsrc_count(struct roc_nix *roc_nix, uint16_t schq[ROC_TM_LVL_MAX])
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
struct mbox *mbox = (&nix->dev)->mbox;
|
|
struct free_rsrcs_rsp *rsp;
|
|
uint8_t hw_lvl;
|
|
int rc, i;
|
|
|
|
/* Get the current free resources */
|
|
mbox_alloc_msg_free_rsrc_cnt(mbox);
|
|
rc = mbox_process_msg(mbox, (void *)&rsp);
|
|
if (rc)
|
|
return rc;
|
|
|
|
for (i = 0; i < ROC_TM_LVL_MAX; i++) {
|
|
hw_lvl = nix_tm_lvl2nix(nix, i);
|
|
if (hw_lvl == NIX_TXSCH_LVL_CNT)
|
|
continue;
|
|
|
|
schq[i] = (nix->is_nix1 ? rsp->schq_nix1[hw_lvl] :
|
|
rsp->schq[hw_lvl]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
roc_nix_tm_rsrc_max(bool pf, uint16_t schq[ROC_TM_LVL_MAX])
|
|
{
|
|
uint8_t hw_lvl, i;
|
|
uint16_t max;
|
|
|
|
for (i = 0; i < ROC_TM_LVL_MAX; i++) {
|
|
hw_lvl = pf ? nix_tm_lvl2nix_tl1_root(i) :
|
|
nix_tm_lvl2nix_tl2_root(i);
|
|
|
|
switch (hw_lvl) {
|
|
case NIX_TXSCH_LVL_SMQ:
|
|
max = (roc_model_is_cn9k() ?
|
|
NIX_CN9K_TXSCH_LVL_SMQ_MAX :
|
|
NIX_TXSCH_LVL_SMQ_MAX);
|
|
break;
|
|
case NIX_TXSCH_LVL_TL4:
|
|
max = NIX_TXSCH_LVL_TL4_MAX;
|
|
break;
|
|
case NIX_TXSCH_LVL_TL3:
|
|
max = NIX_TXSCH_LVL_TL3_MAX;
|
|
break;
|
|
case NIX_TXSCH_LVL_TL2:
|
|
max = pf ? NIX_TXSCH_LVL_TL2_MAX : 1;
|
|
break;
|
|
case NIX_TXSCH_LVL_TL1:
|
|
max = pf ? 1 : 0;
|
|
break;
|
|
default:
|
|
max = 0;
|
|
break;
|
|
}
|
|
schq[i] = max;
|
|
}
|
|
}
|
|
|
|
bool
|
|
roc_nix_tm_root_has_sp(struct roc_nix *roc_nix)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
|
|
if (nix->tm_flags & NIX_TM_TL1_NO_SP)
|
|
return false;
|
|
return true;
|
|
}
|