e746aec161
Fix SQ flush sequence to issue NIX RX SW Sync after SMQ flush. This sync ensures that all the packets that were in-flight are flushed out of memory. This patch also fixes NULL return issues reported by static analysis tool in Traffic Manager and sync's mailbox to that of the kernel version. Fixes:05d727e8b1
("common/cnxk: support NIX traffic management") Fixes:0b7e667ee3
("common/cnxk: enable packet marking") Cc: stable@dpdk.org Signed-off-by: Satha Rao <skoteshwar@marvell.com> Acked-by: Jerin Jacob <jerinj@marvell.com>
305 lines
7.8 KiB
C
305 lines
7.8 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(C) 2022 Marvell.
|
|
*/
|
|
|
|
#include "roc_api.h"
|
|
#include "roc_priv.h"
|
|
|
|
static const uint8_t y_mask_val[ROC_NIX_TM_MARK_MAX][2] = {
|
|
[ROC_NIX_TM_MARK_VLAN_DEI] = {0x0, 0x8},
|
|
[ROC_NIX_TM_MARK_IPV4_DSCP] = {0x1, 0x2},
|
|
[ROC_NIX_TM_MARK_IPV4_ECN] = {0x0, 0xc},
|
|
[ROC_NIX_TM_MARK_IPV6_DSCP] = {0x1, 0x2},
|
|
[ROC_NIX_TM_MARK_IPV6_ECN] = {0x0, 0x3},
|
|
};
|
|
|
|
static const uint8_t r_mask_val[ROC_NIX_TM_MARK_MAX][2] = {
|
|
[ROC_NIX_TM_MARK_VLAN_DEI] = {0x0, 0x8},
|
|
[ROC_NIX_TM_MARK_IPV4_DSCP] = {0x0, 0x3},
|
|
[ROC_NIX_TM_MARK_IPV4_ECN] = {0x0, 0xc},
|
|
[ROC_NIX_TM_MARK_IPV6_DSCP] = {0x0, 0x3},
|
|
[ROC_NIX_TM_MARK_IPV6_ECN] = {0x0, 0x3},
|
|
};
|
|
|
|
static const uint8_t mark_off[ROC_NIX_TM_MARK_MAX] = {
|
|
[ROC_NIX_TM_MARK_VLAN_DEI] = 0x3, /* Byte 14 Bit[4:1] */
|
|
[ROC_NIX_TM_MARK_IPV4_DSCP] = 0x1, /* Byte 1 Bit[6:3] */
|
|
[ROC_NIX_TM_MARK_IPV4_ECN] = 0x6, /* Byte 1 Bit[1:0], Byte 2 Bit[7:6] */
|
|
[ROC_NIX_TM_MARK_IPV6_DSCP] = 0x5, /* Byte 0 Bit[2:0], Byte 1 Bit[7] */
|
|
[ROC_NIX_TM_MARK_IPV6_ECN] = 0x0, /* Byte 1 Bit[7:4] */
|
|
};
|
|
|
|
static const uint64_t mark_flag[ROC_NIX_TM_MARK_MAX] = {
|
|
[ROC_NIX_TM_MARK_VLAN_DEI] = NIX_TM_MARK_VLAN_DEI_EN,
|
|
[ROC_NIX_TM_MARK_IPV4_DSCP] = NIX_TM_MARK_IP_DSCP_EN,
|
|
[ROC_NIX_TM_MARK_IPV4_ECN] = NIX_TM_MARK_IP_ECN_EN,
|
|
[ROC_NIX_TM_MARK_IPV6_DSCP] = NIX_TM_MARK_IP_DSCP_EN,
|
|
[ROC_NIX_TM_MARK_IPV6_ECN] = NIX_TM_MARK_IP_ECN_EN,
|
|
};
|
|
|
|
static uint8_t
|
|
prepare_tm_shaper_red_algo(struct nix_tm_node *tm_node, volatile uint64_t *reg,
|
|
volatile uint64_t *regval,
|
|
volatile uint64_t *regval_mask)
|
|
{
|
|
uint32_t schq = tm_node->hw_id;
|
|
uint8_t k = 0;
|
|
|
|
plt_tm_dbg("Shaper read alg node %s(%u) lvl %u id %u, red_alg %x (%p)",
|
|
nix_tm_hwlvl2str(tm_node->hw_lvl), schq, tm_node->lvl,
|
|
tm_node->id, tm_node->red_algo, tm_node);
|
|
|
|
/* Configure just RED algo */
|
|
regval[k] = ((uint64_t)tm_node->red_algo << 9);
|
|
regval_mask[k] = ~(BIT_ULL(10) | BIT_ULL(9));
|
|
|
|
switch (tm_node->hw_lvl) {
|
|
case NIX_TXSCH_LVL_SMQ:
|
|
reg[k] = NIX_AF_MDQX_SHAPE(schq);
|
|
k++;
|
|
break;
|
|
case NIX_TXSCH_LVL_TL4:
|
|
reg[k] = NIX_AF_TL4X_SHAPE(schq);
|
|
k++;
|
|
break;
|
|
case NIX_TXSCH_LVL_TL3:
|
|
reg[k] = NIX_AF_TL3X_SHAPE(schq);
|
|
k++;
|
|
break;
|
|
case NIX_TXSCH_LVL_TL2:
|
|
reg[k] = NIX_AF_TL2X_SHAPE(schq);
|
|
k++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return k;
|
|
}
|
|
|
|
/* Only called while device is stopped */
|
|
static int
|
|
nix_tm_update_red_algo(struct nix *nix, bool red_send)
|
|
{
|
|
struct mbox *mbox = (&nix->dev)->mbox;
|
|
struct nix_txschq_config *req;
|
|
struct nix_tm_node_list *list;
|
|
struct nix_tm_node *tm_node;
|
|
uint8_t k;
|
|
int rc;
|
|
|
|
list = nix_tm_node_list(nix, nix->tm_tree);
|
|
TAILQ_FOREACH(tm_node, list, node) {
|
|
/* Skip leaf nodes */
|
|
if (nix_tm_is_leaf(nix, tm_node->lvl))
|
|
continue;
|
|
|
|
if (tm_node->hw_lvl == NIX_TXSCH_LVL_TL1)
|
|
continue;
|
|
|
|
/* Skip if no update of red_algo is needed */
|
|
if ((red_send && (tm_node->red_algo == NIX_REDALG_SEND)) ||
|
|
(!red_send && (tm_node->red_algo != NIX_REDALG_SEND)))
|
|
continue;
|
|
|
|
/* Update Red algo */
|
|
if (red_send)
|
|
tm_node->red_algo = NIX_REDALG_SEND;
|
|
else
|
|
tm_node->red_algo = NIX_REDALG_STD;
|
|
|
|
/* Update txschq config */
|
|
req = mbox_alloc_msg_nix_txschq_cfg(mbox);
|
|
if (req == NULL)
|
|
return -ENOSPC;
|
|
|
|
req->lvl = tm_node->hw_lvl;
|
|
k = prepare_tm_shaper_red_algo(tm_node, req->reg, req->regval,
|
|
req->regval_mask);
|
|
req->num_regs = k;
|
|
|
|
rc = mbox_process(mbox);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Return's true if queue reconfig is needed */
|
|
static bool
|
|
nix_tm_update_markfmt(struct nix *nix, enum roc_nix_tm_mark type,
|
|
int mark_yellow, int mark_red)
|
|
{
|
|
uint64_t new_markfmt, old_markfmt;
|
|
uint8_t *tm_markfmt;
|
|
uint8_t en_shift;
|
|
uint64_t mask;
|
|
|
|
if (type >= ROC_NIX_TM_MARK_MAX)
|
|
return false;
|
|
|
|
/* Pre-allocated mark formats for type:color combinations */
|
|
tm_markfmt = nix->tm_markfmt[type];
|
|
|
|
if (!mark_yellow && !mark_red) {
|
|
/* Null format to disable */
|
|
new_markfmt = nix->tm_markfmt_null;
|
|
} else {
|
|
/* Marking enabled with combination of yellow and red */
|
|
if (mark_yellow && mark_red)
|
|
new_markfmt = tm_markfmt[ROC_NIX_TM_MARK_COLOR_Y_R];
|
|
else if (mark_yellow)
|
|
new_markfmt = tm_markfmt[ROC_NIX_TM_MARK_COLOR_Y];
|
|
else
|
|
new_markfmt = tm_markfmt[ROC_NIX_TM_MARK_COLOR_R];
|
|
}
|
|
|
|
mask = 0xFFull;
|
|
/* Format of fast path markfmt
|
|
* ipv6_ecn[8]:ipv4_ecn[8]:ipv6_dscp[8]:ipv4_dscp[8]:vlan_dei[16]
|
|
* fmt[7] = ptr offset for IPv4/IPv6 on l2_len.
|
|
* fmt[6:0] = markfmt idx.
|
|
*/
|
|
switch (type) {
|
|
case ROC_NIX_TM_MARK_VLAN_DEI:
|
|
en_shift = NIX_TM_MARK_VLAN_DEI_SHIFT;
|
|
mask = 0xFFFFull;
|
|
new_markfmt |= new_markfmt << 8;
|
|
break;
|
|
case ROC_NIX_TM_MARK_IPV4_DSCP:
|
|
new_markfmt |= BIT_ULL(7);
|
|
en_shift = NIX_TM_MARK_IPV4_DSCP_SHIFT;
|
|
break;
|
|
case ROC_NIX_TM_MARK_IPV4_ECN:
|
|
new_markfmt |= BIT_ULL(7);
|
|
en_shift = NIX_TM_MARK_IPV4_ECN_SHIFT;
|
|
break;
|
|
case ROC_NIX_TM_MARK_IPV6_DSCP:
|
|
en_shift = NIX_TM_MARK_IPV6_DSCP_SHIFT;
|
|
break;
|
|
case ROC_NIX_TM_MARK_IPV6_ECN:
|
|
new_markfmt |= BIT_ULL(7);
|
|
en_shift = NIX_TM_MARK_IPV6_ECN_SHIFT;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
/* Skip if same as old config */
|
|
old_markfmt = (nix->tm_markfmt_en >> en_shift) & mask;
|
|
if (old_markfmt == new_markfmt)
|
|
return false;
|
|
|
|
/* Need queue reconfig */
|
|
nix->tm_markfmt_en &= ~(mask << en_shift);
|
|
nix->tm_markfmt_en |= (new_markfmt << en_shift);
|
|
|
|
return true;
|
|
}
|
|
|
|
int
|
|
nix_tm_mark_init(struct nix *nix)
|
|
{
|
|
struct mbox *mbox = (&nix->dev)->mbox;
|
|
struct nix_mark_format_cfg_rsp *rsp;
|
|
struct nix_mark_format_cfg *req;
|
|
int rc, i, j;
|
|
|
|
/* Check for supported revisions */
|
|
if (roc_model_is_cn96_ax() || roc_model_is_cn95_a0())
|
|
return 0;
|
|
|
|
/* Null mark format */
|
|
req = mbox_alloc_msg_nix_mark_format_cfg(mbox);
|
|
if (req == NULL)
|
|
return -ENOSPC;
|
|
|
|
rc = mbox_process_msg(mbox, (void *)&rsp);
|
|
if (rc) {
|
|
plt_err("TM failed to alloc null mark format, rc=%d", rc);
|
|
goto exit;
|
|
}
|
|
|
|
nix->tm_markfmt_null = rsp->mark_format_idx;
|
|
|
|
/* Alloc vlan, dscp, ecn mark formats */
|
|
for (i = 0; i < ROC_NIX_TM_MARK_MAX; i++) {
|
|
for (j = 0; j < ROC_NIX_TM_MARK_COLOR_MAX; j++) {
|
|
req = mbox_alloc_msg_nix_mark_format_cfg(mbox);
|
|
if (req == NULL)
|
|
return -ENOSPC;
|
|
|
|
req->offset = mark_off[i];
|
|
|
|
switch (j) {
|
|
case ROC_NIX_TM_MARK_COLOR_Y:
|
|
req->y_mask = y_mask_val[i][0];
|
|
req->y_val = y_mask_val[i][1];
|
|
break;
|
|
case ROC_NIX_TM_MARK_COLOR_R:
|
|
req->r_mask = r_mask_val[i][0];
|
|
req->r_val = r_mask_val[i][1];
|
|
break;
|
|
case ROC_NIX_TM_MARK_COLOR_Y_R:
|
|
req->y_mask = y_mask_val[i][0];
|
|
req->y_val = y_mask_val[i][1];
|
|
req->r_mask = r_mask_val[i][0];
|
|
req->r_val = r_mask_val[i][1];
|
|
break;
|
|
}
|
|
|
|
rc = mbox_process_msg(mbox, (void *)&rsp);
|
|
if (rc) {
|
|
plt_err("TM failed to alloc mark fmt "
|
|
"type %u color %u, rc=%d",
|
|
i, j, rc);
|
|
goto exit;
|
|
}
|
|
|
|
nix->tm_markfmt[i][j] = rsp->mark_format_idx;
|
|
plt_tm_dbg("Mark type: %u, Mark Color:%u, id:%u\n", i,
|
|
j, nix->tm_markfmt[i][j]);
|
|
}
|
|
}
|
|
/* Update null mark format as default */
|
|
nix_tm_update_markfmt(nix, ROC_NIX_TM_MARK_VLAN_DEI, 0, 0);
|
|
nix_tm_update_markfmt(nix, ROC_NIX_TM_MARK_IPV4_DSCP, 0, 0);
|
|
nix_tm_update_markfmt(nix, ROC_NIX_TM_MARK_IPV4_ECN, 0, 0);
|
|
nix_tm_update_markfmt(nix, ROC_NIX_TM_MARK_IPV6_DSCP, 0, 0);
|
|
nix_tm_update_markfmt(nix, ROC_NIX_TM_MARK_IPV6_ECN, 0, 0);
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
roc_nix_tm_mark_config(struct roc_nix *roc_nix, enum roc_nix_tm_mark type,
|
|
int mark_yellow, int mark_red)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
int rc;
|
|
|
|
if (!(nix->tm_flags & NIX_TM_HIERARCHY_ENA))
|
|
return -EINVAL;
|
|
|
|
rc = nix_tm_update_markfmt(nix, type, mark_yellow, mark_red);
|
|
if (!rc)
|
|
return 0;
|
|
|
|
if (!mark_yellow && !mark_red)
|
|
nix->tm_flags &= ~mark_flag[type];
|
|
else
|
|
nix->tm_flags |= mark_flag[type];
|
|
|
|
/* Update red algo for change in mark_red */
|
|
return nix_tm_update_red_algo(nix, !!mark_red);
|
|
}
|
|
|
|
uint64_t
|
|
roc_nix_tm_mark_format_get(struct roc_nix *roc_nix, uint64_t *flags)
|
|
{
|
|
struct nix *nix = roc_nix_to_nix_priv(roc_nix);
|
|
|
|
*flags = ((nix->tm_flags & NIX_TM_MARK_EN_MASK) >> 3);
|
|
return nix->tm_markfmt_en;
|
|
}
|