/* MTU used for DWRR calculation is in power of 2 up until 64K bytes. * Value of 4 is reserved for MTU value of 9728 bytes. * Value of 5 is reserved for MTU value of 10240 bytes.
*/ switch (dwrr_mtu) { case 4: return 9728; case 5: return 10240; default: return BIT_ULL(dwrr_mtu);
}
return 0;
}
u32 convert_bytes_to_dwrr_mtu(u32 bytes)
{ /* MTU used for DWRR calculation is in power of 2 up until 64K bytes. * Value of 4 is reserved for MTU value of 9728 bytes. * Value of 5 is reserved for MTU value of 10240 bytes.
*/ if (bytes > BIT_ULL(16)) return 0;
switch (bytes) { case 9728: return 4; case 10240: return 5; default: return ilog2(bytes);
}
return 0;
}
staticvoid nix_rx_sync(struct rvu *rvu, int blkaddr)
{ int err;
/* Sync all in flight RX packets to LLC/DRAM */
rvu_write64(rvu, blkaddr, NIX_AF_RX_SW_SYNC, BIT_ULL(0));
err = rvu_poll_reg(rvu, blkaddr, NIX_AF_RX_SW_SYNC, BIT_ULL(0), true); if (err)
dev_err(rvu->dev, "SYNC1: NIX RX software sync failed\n");
/* SW_SYNC ensures all existing transactions are finished and pkts * are written to LLC/DRAM, queues should be teared down after * successful SW_SYNC. Due to a HW errata, in some rare scenarios * an existing transaction might end after SW_SYNC operation. To * ensure operation is fully done, do the SW_SYNC twice.
*/
rvu_write64(rvu, blkaddr, NIX_AF_RX_SW_SYNC, BIT_ULL(0));
err = rvu_poll_reg(rvu, blkaddr, NIX_AF_RX_SW_SYNC, BIT_ULL(0), true); if (err)
dev_err(rvu->dev, "SYNC2: NIX RX software sync failed\n");
}
/* If NIX1 block is present on the silicon then NIXes are * assigned alternatively for lbk interfaces. NIX0 should * send packets on lbk link 1 channels and NIX1 should send * on lbk link 0 channels for the communication between * NIX0 and NIX1.
*/
lbkid = 0; if (rvu->hw->lbk_links > 1)
lbkid = vf & 0x1 ? 0 : 1;
/* By default NIX0 is configured to send packet on lbk link 1 * (which corresponds to LBK1), same packet will receive on * NIX1 over lbk link 0. If NIX1 sends packet on lbk link 0 * (which corresponds to LBK2) packet will receive on NIX0 lbk * link 1. * But if lbk links for NIX0 and NIX1 are negated, i.e NIX0 * transmits and receives on lbk link 0, whick corresponds * to LBK1 block, back to back connectivity between NIX and * LBK can be achieved (which is similar to 96xx) * * RX TX * NIX0 lbk link 1 (LBK2) 1 (LBK1) * NIX0 lbk link 0 (LBK0) 0 (LBK0) * NIX1 lbk link 0 (LBK1) 0 (LBK2) * NIX1 lbk link 1 (LBK3) 1 (LBK3)
*/ if (loop)
lbkid = !lbkid;
/* Note that AF's VFs work in pairs and talk over consecutive * loopback channels.Therefore if odd number of AF VFs are * enabled then the last VF remains with no pair.
*/
pfvf->rx_chan_base = rvu_nix_chan_lbk(rvu, lbkid, vf);
pfvf->tx_chan_base = vf & 0x1 ?
rvu_nix_chan_lbk(rvu, lbkid, vf - 1) :
rvu_nix_chan_lbk(rvu, lbkid, vf + 1);
pfvf->rx_chan_cnt = 1;
pfvf->tx_chan_cnt = 1;
rsp->tx_link = hw->cgx_links + lbkid;
pfvf->lbkid = lbkid;
rvu_npc_set_pkind(rvu, NPC_RX_LBK_PKIND, pfvf);
rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
pfvf->rx_chan_base,
pfvf->rx_chan_cnt);
/* Add a UCAST forwarding rule in MCAM with this NIXLF attached * RVU PF/VF's MAC address.
*/
rvu_npc_install_ucast_entry(rvu, pcifunc, nixlf,
pfvf->rx_chan_base, pfvf->mac_addr);
/* Add this PF_FUNC to bcast pkt replication list */
err = nix_update_mce_rule(rvu, pcifunc, NIXLF_BCAST_ENTRY, true); if (err) {
dev_err(rvu->dev, "Bcast list, failed to enable PF_FUNC 0x%x\n",
pcifunc); return err;
} /* Install MCAM rule matching Ethernet broadcast mac address */
rvu_npc_install_bcast_match_entry(rvu, pcifunc,
nixlf, pfvf->rx_chan_base);
err = nix_get_struct_ptrs(rvu, pcifunc, &nix_hw, &blkaddr); if (err) return;
bp = &nix_hw->bp;
mutex_lock(&rvu->rsrc_lock); for (bpid = 0; bpid < bp->bpids.max; bpid++) { if (bp->fn_map[bpid] == pcifunc) {
bp->ref_cnt[bpid]--; if (bp->ref_cnt[bpid]) continue;
rvu_free_rsrc(&bp->bpids, bpid);
bp->fn_map[bpid] = 0;
}
}
mutex_unlock(&rvu->rsrc_lock);
}
static u16 nix_get_channel(u16 chan, bool cpt_link)
{ /* CPT channel for a given link channel is always * assumed to be BIT(11) set in link channel.
*/ return cpt_link ? chan | BIT(11) : chan;
}
pf = rvu_get_pf(rvu->pdev, pcifunc);
type = is_lbk_vf(rvu, pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; if (is_sdp_pfvf(rvu, pcifunc))
type = NIX_INTF_TYPE_SDP;
/* Enable backpressure only for CGX mapped PFs and LBK/SDP interface */ if (!is_pf_cgxmapped(rvu, pf) && type != NIX_INTF_TYPE_LBK &&
type != NIX_INTF_TYPE_SDP) return 0;
/* Set rest of the fields to NOP */ for (; fidx < 8; fidx++) {
rvu_write64(rvu, blkaddr,
NIX_AF_LSO_FORMATX_FIELDX(idx, fidx), 0x0ULL);
}
nix_hw->lso.in_use++;
/* Set rest of the fields to NOP */ for (; fidx < 8; fidx++) {
rvu_write64(rvu, blkaddr,
NIX_AF_LSO_FORMATX_FIELDX(idx, fidx), 0x0ULL);
}
nix_hw->lso.in_use++;
}
staticvoid nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
{
kfree(pfvf->rq_bmap);
kfree(pfvf->sq_bmap);
kfree(pfvf->cq_bmap); if (pfvf->rq_ctx)
qmem_free(rvu->dev, pfvf->rq_ctx); if (pfvf->sq_ctx)
qmem_free(rvu->dev, pfvf->sq_ctx); if (pfvf->cq_ctx)
qmem_free(rvu->dev, pfvf->cq_ctx); if (pfvf->rss_ctx)
qmem_free(rvu->dev, pfvf->rss_ctx); if (pfvf->nix_qints_ctx)
qmem_free(rvu->dev, pfvf->nix_qints_ctx); if (pfvf->cq_ints_ctx)
qmem_free(rvu->dev, pfvf->cq_ints_ctx);
staticint nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr, struct rvu_pfvf *pfvf, int nixlf, int rss_sz, int rss_grps, int hwctx_size,
u64 way_mask, bool tag_lsb_as_adder)
{ int err, grp, num_indices;
u64 val;
/* RSS is not requested for this NIXLF */ if (!rss_sz) return 0;
num_indices = rss_sz * rss_grps;
/* Alloc NIX RSS HW context memory and config the base */
err = qmem_alloc(rvu->dev, &pfvf->rss_ctx, num_indices, hwctx_size); if (err) return err;
/* Ring the doorbell and wait for result */
rvu_write64(rvu, block->addr, NIX_AF_AQ_DOOR, 1); while (result->compcode == NIX_AQ_COMP_NOTDONE) {
cpu_relax();
udelay(1);
timeout--; if (!timeout) return -EBUSY;
}
if (result->compcode != NIX_AQ_COMP_GOOD) { /* TODO: Replace this with some error code */ if (result->compcode == NIX_AQ_COMP_CTX_FAULT ||
result->compcode == NIX_AQ_COMP_LOCKERR ||
result->compcode == NIX_AQ_COMP_CTX_POISON) {
ret = rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX0_RX);
ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX0_TX);
ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX1_RX);
ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX1_TX); if (ret)
dev_err(rvu->dev, "%s: Not able to unlock cachelines\n", __func__);
}
/* Skip NIXLF check for broadcast MCE entry and bandwidth profile * operations done by AF itself.
*/ if (!((!rsp && req->ctype == NIX_AQ_CTYPE_MCE) ||
(req->ctype == NIX_AQ_CTYPE_BANDPROF && !pcifunc))) { if (!pfvf->nixlf || nixlf < 0) return NIX_AF_ERR_AF_LF_INVALID;
}
switch (req->ctype) { case NIX_AQ_CTYPE_RQ: /* Check if index exceeds max no of queues */ if (!pfvf->rq_ctx || req->qidx >= pfvf->rq_ctx->qsize)
rc = NIX_AF_ERR_AQ_ENQUEUE; break; case NIX_AQ_CTYPE_SQ: if (!pfvf->sq_ctx || req->qidx >= pfvf->sq_ctx->qsize)
rc = NIX_AF_ERR_AQ_ENQUEUE; break; case NIX_AQ_CTYPE_CQ: if (!pfvf->cq_ctx || req->qidx >= pfvf->cq_ctx->qsize)
rc = NIX_AF_ERR_AQ_ENQUEUE; break; case NIX_AQ_CTYPE_RSS: /* Check if RSS is enabled and qidx is within range */
cfg = rvu_read64(rvu, blkaddr, NIX_AF_LFX_RSS_CFG(nixlf)); if (!(cfg & BIT_ULL(4)) || !pfvf->rss_ctx ||
(req->qidx >= (256UL << (cfg & 0xF))))
rc = NIX_AF_ERR_AQ_ENQUEUE; break; case NIX_AQ_CTYPE_MCE:
cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_MCAST_CFG);
/* Check if index exceeds MCE list length */ if (!nix_hw->mcast.mce_ctx ||
(req->qidx >= (256UL << (cfg & 0xF))))
rc = NIX_AF_ERR_AQ_ENQUEUE;
/* Adding multicast lists for requests from PF/VFs is not * yet supported, so ignore this.
*/ if (rsp)
rc = NIX_AF_ERR_AQ_ENQUEUE; break; case NIX_AQ_CTYPE_BANDPROF: if (nix_verify_bandprof((struct nix_cn10k_aq_enq_req *)req,
nix_hw, pcifunc))
rc = NIX_AF_ERR_INVALID_BANDPROF; break; default:
rc = NIX_AF_ERR_AQ_ENQUEUE;
}
if (rc) return rc;
nix_get_aq_req_smq(rvu, req, &smq, &smq_mask); /* Check if SQ pointed SMQ belongs to this PF/VF or not */ if (req->ctype == NIX_AQ_CTYPE_SQ &&
((req->op == NIX_AQ_INSTOP_INIT && req->sq.ena) ||
(req->op == NIX_AQ_INSTOP_WRITE &&
req->sq_mask.ena && req->sq.ena && smq_mask))) { if (!is_valid_txschq(rvu, blkaddr, NIX_TXSCH_LVL_SMQ,
pcifunc, smq)) return NIX_AF_ERR_AQ_ENQUEUE;
}
memset(&inst, 0, sizeof(struct nix_aq_inst_s));
inst.lf = nixlf;
inst.cindex = req->qidx;
inst.ctype = req->ctype;
inst.op = req->op; /* Currently we are not supporting enqueuing multiple instructions, * so always choose first entry in result memory.
*/
inst.res_addr = (u64)aq->res->iova;
/* Hardware uses same aq->res->base for updating result of * previous instruction hence wait here till it is done.
*/
spin_lock(&aq->lock);
/* Clean result + context memory */
memset(aq->res->base, 0, aq->res->entry_sz); /* Context needs to be written at RES_ADDR + 128 */
ctx = aq->res->base + 128; /* Mask needs to be written at RES_ADDR + 256 */
mask = aq->res->base + 256;
rc = nix_aq_context_read(rvu, nix_hw, &aq_req, &aq_rsp,
req->hdr.pcifunc, ctype, req->qidx); if (rc) {
dev_err(rvu->dev, "%s: Failed to fetch %s%d context of PFFUNC 0x%x\n",
__func__, nix_get_ctx_name(ctype), req->qidx,
req->hdr.pcifunc); return rc;
}
/* Make copy of original context & mask which are required * for resubmission
*/
memcpy(&aq_req.cq_mask, &req->cq_mask, sizeof(struct nix_cq_ctx_s));
memcpy(&aq_req.cq, &req->cq, sizeof(struct nix_cq_ctx_s));
/* Context mask (cq_mask) holds mask value of fields which * are changed in AQ WRITE operation. * for example cq.drop = 0xa; * cq_mask.drop = 0xff; * Below logic performs '&' between cq and cq_mask so that non * updated fields are masked out for request and response * comparison
*/ for (word = 0; word < sizeof(struct nix_cq_ctx_s) / sizeof(u64);
word++) {
*(u64 *)((u8 *)&aq_rsp.cq + word * 8) &=
(*(u64 *)((u8 *)&aq_req.cq_mask + word * 8));
*(u64 *)((u8 *)&aq_req.cq + word * 8) &=
(*(u64 *)((u8 *)&aq_req.cq_mask + word * 8));
}
if (memcmp(&aq_req.cq, &aq_rsp.cq, sizeof(struct nix_cq_ctx_s))) return NIX_AF_ERR_AQ_CTX_RETRY_WRITE;
/* HW errata 'AQ Modification to CQ could be discarded on heavy traffic' * As a work around perfrom CQ context read after each AQ write. If AQ * read shows AQ write is not updated perform AQ write again.
*/ if (!err && req->op == NIX_AQ_INSTOP_WRITE) {
err = rvu_nix_verify_aq_ctx(rvu, nix_hw, req, NIX_AQ_CTYPE_CQ); if (err == NIX_AF_ERR_AQ_CTX_RETRY_WRITE) { if (retries--) goto retry; else return NIX_AF_ERR_CQ_CTX_WRITE_ERR;
}
}
return err;
}
staticconstchar *nix_get_ctx_name(int ctype)
{ switch (ctype) { case NIX_AQ_CTYPE_CQ: return"CQ"; case NIX_AQ_CTYPE_SQ: return"SQ"; case NIX_AQ_CTYPE_RQ: return"RQ"; case NIX_AQ_CTYPE_RSS: return"RSS";
} return"";
}
/* Check if requested 'NIXLF <=> NPALF' mapping is valid */ if (req->npa_func) { /* If default, use 'this' NIXLF's PFFUNC */ if (req->npa_func == RVU_DEFAULT_PF_FUNC)
req->npa_func = pcifunc; if (!is_pffunc_map_valid(rvu, req->npa_func, BLKTYPE_NPA)) return NIX_AF_INVAL_NPA_PF_FUNC;
}
/* Check if requested 'NIXLF <=> SSOLF' mapping is valid */ if (req->sso_func) { /* If default, use 'this' NIXLF's PFFUNC */ if (req->sso_func == RVU_DEFAULT_PF_FUNC)
req->sso_func = pcifunc; if (!is_pffunc_map_valid(rvu, req->sso_func, BLKTYPE_SSO)) return NIX_AF_INVAL_SSO_PF_FUNC;
}
/* If RSS is being enabled, check if requested config is valid. * RSS table size should be power of two, otherwise * RSS_GRP::OFFSET + adder might go beyond that group or * won't be able to use entire table.
*/ if (req->rss_sz && (req->rss_sz > MAX_RSS_INDIR_TBL_SIZE ||
!is_power_of_2(req->rss_sz))) return NIX_AF_ERR_RSS_SIZE_INVALID;
if (req->rss_sz &&
(!req->rss_grps || req->rss_grps > MAX_RSS_GROUPS)) return NIX_AF_ERR_RSS_GRPS_INVALID;
/* Reset this NIX LF */
err = rvu_lf_reset(rvu, block, nixlf); if (err) {
dev_err(rvu->dev, "Failed to reset NIX%d LF%d\n",
block->addr - BLKADDR_NIX0, nixlf); return NIX_AF_ERR_LF_RESET;
}
/* Setup VLANX TPID's. * Use VLAN1 for 802.1Q * and VLAN0 for 802.1AD.
*/
cfg = (0x8100ULL << 16) | 0x88A8ULL;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf), cfg);
/* Enable LMTST for this NIX LF */
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG2(nixlf), BIT_ULL(0));
/* Set CQE/WQE size, NPA_PF_FUNC for SQBs and also SSO_PF_FUNC */ if (req->npa_func)
cfg = req->npa_func; if (req->sso_func)
cfg |= (u64)req->sso_func << 16;
/* Nothing special to do when state is not toggled */
oldval = rvu_read64(rvu, blkaddr, reg); if ((oldval & 0x1) == (regval & 0x1)) {
rvu_write64(rvu, blkaddr, reg, regval); returntrue;
}
staticvoid nix_reset_tx_schedule(struct rvu *rvu, int blkaddr, int lvl, int schq)
{
u64 tlx_parent = 0, tlx_schedule = 0;
switch (lvl) { case NIX_TXSCH_LVL_TL2:
tlx_parent = NIX_AF_TL2X_PARENT(schq);
tlx_schedule = NIX_AF_TL2X_SCHEDULE(schq); break; case NIX_TXSCH_LVL_TL3:
tlx_parent = NIX_AF_TL3X_PARENT(schq);
tlx_schedule = NIX_AF_TL3X_SCHEDULE(schq); break; case NIX_TXSCH_LVL_TL4:
tlx_parent = NIX_AF_TL4X_PARENT(schq);
tlx_schedule = NIX_AF_TL4X_SCHEDULE(schq); break; case NIX_TXSCH_LVL_MDQ: /* no need to reset SMQ_CFG as HW clears this CSR * on SMQ flush
*/
tlx_parent = NIX_AF_MDQX_PARENT(schq);
tlx_schedule = NIX_AF_MDQX_SCHEDULE(schq); break; default: return;
}
if (tlx_parent)
rvu_write64(rvu, blkaddr, tlx_parent, 0x0);
if (tlx_schedule)
rvu_write64(rvu, blkaddr, tlx_schedule, 0x0);
}
/* Disable shaping of pkts by a scheduler queue * at a given scheduler level.
*/ staticvoid nix_reset_tx_shaping(struct rvu *rvu, int blkaddr, int nixlf, int lvl, int schq)
{ struct rvu_hwinfo *hw = rvu->hw;
u64 cir_reg = 0, pir_reg = 0;
u64 cfg;
switch (lvl) { case NIX_TXSCH_LVL_TL1:
cir_reg = NIX_AF_TL1X_CIR(schq);
pir_reg = 0; /* PIR not available at TL1 */ break; case NIX_TXSCH_LVL_TL2:
cir_reg = NIX_AF_TL2X_CIR(schq);
pir_reg = NIX_AF_TL2X_PIR(schq); break; case NIX_TXSCH_LVL_TL3:
cir_reg = NIX_AF_TL3X_CIR(schq);
pir_reg = NIX_AF_TL3X_PIR(schq); break; case NIX_TXSCH_LVL_TL4:
cir_reg = NIX_AF_TL4X_CIR(schq);
pir_reg = NIX_AF_TL4X_PIR(schq); break; case NIX_TXSCH_LVL_MDQ:
cir_reg = NIX_AF_MDQX_CIR(schq);
pir_reg = NIX_AF_MDQX_PIR(schq); break;
}
/* Shaper state toggle needs wait/poll */ if (hw->cap.nix_shaper_toggle_wait) { if (cir_reg)
handle_txschq_shaper_update(rvu, blkaddr, nixlf,
lvl, cir_reg, 0); if (pir_reg)
handle_txschq_shaper_update(rvu, blkaddr, nixlf,
lvl, pir_reg, 0); return;
}
/* For traffic aggregating scheduler level, one queue is enough */ if (lvl >= hw->cap.nix_tx_aggr_lvl) { if (req_schq != 1) return NIX_AF_ERR_TLX_ALLOC_FAIL; return 0;
}
/* Get free SCHQ count and check if request can be accomodated */ if (hw->cap.nix_fixed_txschq_mapping) {
nix_get_txschq_range(rvu, pcifunc, link, &start, &end);
schq = start + (pcifunc & RVU_PFVF_FUNC_MASK); if (end <= txsch->schq.max && schq < end &&
!test_bit(schq, txsch->schq.bmap))
free_cnt = 1; else
free_cnt = 0;
} else {
free_cnt = rvu_rsrc_free_count(&txsch->schq);
}
/* If contiguous queues are needed, check for availability */ if (!hw->cap.nix_fixed_txschq_mapping && req->schq_contig[lvl] &&
!rvu_rsrc_check_contig(&txsch->schq, req->schq_contig[lvl])) return NIX_AF_ERR_TLX_ALLOC_FAIL;
return 0;
}
staticvoid nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch, struct nix_txsch_alloc_rsp *rsp, int lvl, int start, int end)
{ struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = rsp->hdr.pcifunc; int idx, schq;
/* For traffic aggregating levels, queue alloc is based * on transmit link to which PF_FUNC is mapped to.
*/ if (lvl >= hw->cap.nix_tx_aggr_lvl) { /* A single TL queue is allocated */ if (rsp->schq_contig[lvl]) {
rsp->schq_contig[lvl] = 1;
rsp->schq_contig_list[lvl][0] = start;
}
/* Both contig and non-contig reqs doesn't make sense here */ if (rsp->schq_contig[lvl])
rsp->schq[lvl] = 0;
rc = nix_get_nixlf(rvu, pcifunc, &nixlf, &blkaddr); if (rc) return rc;
nix_hw = get_nix_hw(rvu->hw, blkaddr); if (!nix_hw) return NIX_AF_ERR_INVALID_NIXBLK;
mutex_lock(&rvu->rsrc_lock);
/* Check if request is valid as per HW capabilities * and can be accomodated.
*/ for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
rc = nix_check_txschq_alloc_req(rvu, lvl, pcifunc, nix_hw, req); if (rc) goto err;
}
/* On PF cleanup, clear cfg done flag as * PF would have changed default config.
*/ if (!(pcifunc & RVU_PFVF_FUNC_MASK)) {
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL1];
schq = nix_get_tx_link(rvu, pcifunc); /* Do not clear pcifunc in txsch->pfvf_map[schq] because * VF might be using this TL1 queue
*/
map_func = TXSCH_MAP_FUNC(txsch->pfvf_map[schq]);
txsch->pfvf_map[schq] = TXSCH_SET_FLAG(map_func, 0x0);
}
/* Now free scheduler queues to free pool */ for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { /* TLs above aggregation level are shared across all PF * and it's VFs, hence skip freeing them.
*/ if (lvl >= hw->cap.nix_tx_aggr_lvl) continue;
/* Clear SW_XOFF of this resource only. * For SMQ level, all path XOFF's * need to be made clear by user
*/
nix_clear_tx_xoff(rvu, blkaddr, lvl, schq);
/* Flush if it is a SMQ. Onus of disabling * TL2/3 queue links before SMQ flush is on user
*/ if (lvl == NIX_TXSCH_LVL_SMQ &&
nix_smq_flush(rvu, blkaddr, schq, pcifunc, nixlf)) {
rc = NIX_AF_SMQ_FLUSH_FAILED; goto err;
}
if (!rvu_check_valid_reg(TXSCHQ_HWREGMAP, lvl, reg)) returnfalse;
schq = TXSCHQ_IDX(reg, TXSCHQ_IDX_SHIFT); /* Check if this schq belongs to this PF/VF or not */ if (!is_valid_txschq(rvu, blkaddr, lvl, pcifunc, schq)) returnfalse;
/* If shaping and coloring is not supported, then * *_CIR and *_PIR registers should not be configured.
*/
regbase = reg & 0xFFFF;
switch (lvl) { case NIX_TXSCH_LVL_TL1: if (regbase == NIX_AF_TL1X_CIR(0)) returnfalse; break; case NIX_TXSCH_LVL_TL2: if (regbase == NIX_AF_TL2X_CIR(0) ||
regbase == NIX_AF_TL2X_PIR(0)) returnfalse; break; case NIX_TXSCH_LVL_TL3: if (regbase == NIX_AF_TL3X_CIR(0) ||
regbase == NIX_AF_TL3X_PIR(0)) returnfalse; break; case NIX_TXSCH_LVL_TL4: if (regbase == NIX_AF_TL4X_CIR(0) ||
regbase == NIX_AF_TL4X_PIR(0)) returnfalse; break; case NIX_TXSCH_LVL_MDQ: if (regbase == NIX_AF_MDQX_CIR(0) ||
regbase == NIX_AF_MDQX_PIR(0)) returnfalse; break;
} returntrue;
}
staticvoid nix_tl1_default_cfg(struct rvu *rvu, struct nix_hw *nix_hw,
u16 pcifunc, int blkaddr)
{
u32 *pfvf_map; int schq;
schq = nix_get_tx_link(rvu, pcifunc);
pfvf_map = nix_hw->txsch[NIX_TXSCH_LVL_TL1].pfvf_map; /* Skip if PF has already done the config */ if (TXSCH_MAP_FLAGS(pfvf_map[schq]) & NIX_TXSCHQ_CFG_DONE) return;
rvu_write64(rvu, blkaddr, NIX_AF_TL1X_TOPOLOGY(schq),
(TXSCH_TL1_DFLT_RR_PRIO << 1));
/* On OcteonTx2 the config was in bytes and newer silcons * it's changed to weight.
*/ if (!rvu->hw->cap.nix_common_dwrr_mtu)
rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SCHEDULE(schq),
TXSCH_TL1_DFLT_RR_QTM); else
rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SCHEDULE(schq),
CN10K_MAX_DWRR_WEIGHT);
for (schq = 0; schq < txsch->schq.max; schq++) { if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc) continue; /* Enable all LBK links with channel 63 by default so that * packets can be sent to LBK with a NPC TX MCAM rule
*/
lbk_links = hw->lbk_links; while (lbk_links--)
rvu_write64(rvu, blkaddr,
NIX_AF_TL3_TL2X_LINKX_CFG(schq,
lbk_link_start +
lbk_links), cfg);
}
}
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); if (blkaddr < 0) return;
nix_hw = get_nix_hw(rvu->hw, blkaddr); if (!nix_hw) return;
vlan = &nix_hw->txvlan;
mutex_lock(&vlan->rsrc_lock); /* Scan all the entries and free the ones mapped to 'pcifunc' */ for (index = 0; index < vlan->rsrc.max; index++) { if (vlan->entry2pfvf_map[index] == pcifunc)
nix_tx_vtag_free(rvu, blkaddr, pcifunc, index);
}
mutex_unlock(&vlan->rsrc_lock);
}
mce_list = &elem->mcast_mce_list; for (i = 0; i < num_entry; i++) {
is_found = false;
hlist_for_each_entry(mce, &mce_list->head, node) { /* If already exists, then delete */ if (mce->pcifunc == req->pcifunc[i]) {
hlist_del(&mce->node);
kfree(mce);
mce_list->count--;
is_found = true; break;
}
}
if (!is_found) return NIX_AF_ERR_INVALID_MCAST_DEL_REQ;
}
mce_list->max = mce_list->count; /* Dump the updated list to HW */ if (elem->dir == NIX_MCAST_INGRESS) return nix_update_ingress_mce_list_hw(rvu, nix_hw, elem);
/* Scan through the current list */
hlist_for_each_entry(mce, &mce_list->head, node) { /* If already exists, then delete */ if (mce->pcifunc == pcifunc && !add) { delete = true; break;
} elseif (mce->pcifunc == pcifunc && add) { /* entry already exists */ return 0;
}
tail = mce;
}
if (delete) {
hlist_del(&mce->node);
kfree(mce);
mce_list->count--; return 0;
}
if (!add) return 0;
/* Add a new one to the list, at the tail */
mce = kzalloc(sizeof(*mce), GFP_KERNEL); if (!mce) return -ENOMEM;
mce->pcifunc = pcifunc; if (!tail)
hlist_add_head(&mce->node, &mce_list->head); else
hlist_add_behind(&mce->node, &tail->node);
mce_list->count++; return 0;
}
int nix_update_mce_list(struct rvu *rvu, u16 pcifunc, struct nix_mce_list *mce_list, int mce_idx, int mcam_index, bool add)
{ int err = 0, idx, next_idx, last_idx, blkaddr, npc_blkaddr; struct npc_mcam *mcam = &rvu->hw->mcam; struct nix_mcast *mcast; struct nix_hw *nix_hw; struct mce *mce;
if (!mce_list) return -EINVAL;
/* Get this PF/VF func's MCE index */
idx = mce_idx + (pcifunc & RVU_PFVF_FUNC_MASK);
if (idx > (mce_idx + mce_list->max)) {
dev_err(rvu->dev, "%s: Idx %d > max MCE idx %d, for PF%d bcast list\n",
__func__, idx, mce_list->max,
rvu_get_pf(rvu->pdev, pcifunc)); return -EINVAL;
}
err = nix_get_struct_ptrs(rvu, pcifunc, &nix_hw, &blkaddr); if (err) return err;
/* Skip PF0 (i.e AF) */ for (pf = 1; pf < (rvu->cgx_mapped_pfs + 1); pf++) {
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf)); /* If PF is not enabled, nothing to do */ if (!((cfg >> 20) & 0x01)) continue; /* Get numVFs attached to this PF */
numvfs = (cfg >> 12) & 0xFF;
pfvf = &rvu->pf[pf];
/* This NIX0/1 block mapped to PF ? */ if (pfvf->nix_blkaddr != nix_hw->blkaddr) continue;
/* save start idx of broadcast mce list */
pfvf->bcast_mce_idx = nix_alloc_mce_list(mcast, numvfs + 1, NIX_MCAST_INGRESS);
nix_mce_list_init(&pfvf->bcast_mce_list, numvfs + 1);
/* save start idx of multicast mce list */
pfvf->mcast_mce_idx = nix_alloc_mce_list(mcast, numvfs + 1, NIX_MCAST_INGRESS);
nix_mce_list_init(&pfvf->mcast_mce_list, numvfs + 1);
/* save the start idx of promisc mce list */
pfvf->promisc_mce_idx = nix_alloc_mce_list(mcast, numvfs + 1, NIX_MCAST_INGRESS);
nix_mce_list_init(&pfvf->promisc_mce_list, numvfs + 1);
for (idx = 0; idx < (numvfs + 1); idx++) { /* idx-0 is for PF, followed by VFs */
pcifunc = rvu_make_pcifunc(rvu->pdev, pf, 0);
pcifunc |= idx; /* Add dummy entries now, so that we don't have to check * for whether AQ_OP should be INIT/WRITE later on. * Will be updated when a NIXLF is attached/detached to * these PF/VFs.
*/
err = nix_blk_setup_mce(rvu, nix_hw,
pfvf->bcast_mce_idx + idx,
NIX_AQ_INSTOP_INIT,
pcifunc, 0, 0, 1, true); if (err) return err;
/* add dummy entries to multicast mce list */
err = nix_blk_setup_mce(rvu, nix_hw,
pfvf->mcast_mce_idx + idx,
NIX_AQ_INSTOP_INIT,
pcifunc, 0, 0, 1, true); if (err) return err;
/* Set max list length equal to max no of VFs per PF + PF itself */
rvu_write64(rvu, blkaddr, NIX_AF_RX_MCAST_CFG,
BIT_ULL(36) | (hw->max_vfs_per_pf << 4) | MC_TBL_SIZE);
int rvu_mbox_handler_nix_stats_rst(struct rvu *rvu, struct msg_req *req, struct msg_rsp *rsp)
{
u16 pcifunc = req->hdr.pcifunc; int i, nixlf, blkaddr, err;
u64 stats;
err = nix_get_nixlf(rvu, pcifunc, &nixlf, &blkaddr); if (err) return err;
/* Get stats count supported by HW */
stats = rvu_read64(rvu, blkaddr, NIX_AF_CONST1);
/* Reset tx stats */ for (i = 0; i < ((stats >> 24) & 0xFF); i++)
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_STATX(nixlf, i), 0);
/* Reset rx stats */ for (i = 0; i < ((stats >> 32) & 0xFF); i++)
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_STATX(nixlf, i), 0);
return 0;
}
/* Returns the ALG index to be set into NPC_RX_ACTION */ staticint get_flowkey_alg_idx(struct nix_hw *nix_hw, u32 flow_cfg)
{ int i;
/* Scan over exiting algo entries to find a match */ for (i = 0; i < nix_hw->flowkey.in_use; i++) if (nix_hw->flowkey.flowkey[i] == flow_cfg) return i;
return -ERANGE;
}
/* Mask to match ipv6(NPC_LT_LC_IP6) and ipv6 ext(NPC_LT_LC_IP6_EXT) */ #define NPC_LT_LC_IP6_MATCH_MSK ((~(NPC_LT_LC_IP6 ^ NPC_LT_LC_IP6_EXT)) & 0xf) /* Mask to match both ipv4(NPC_LT_LC_IP) and ipv4 ext(NPC_LT_LC_IP_OPT) */ #define NPC_LT_LC_IP_MATCH_MSK ((~(NPC_LT_LC_IP ^ NPC_LT_LC_IP_OPT)) & 0xf)
/* Each of the 32 possible flow key algorithm definitions should * fall into above incremental config (except ALG0). Otherwise a * single NPC MCAM entry is not sufficient for supporting RSS. * * If a different definition or combination needed then NPC MCAM * has to be programmed to filter such pkts and it's action should * point to this definition to calculate flowtag or hash. * * The `for loop` goes over _all_ protocol field and the following * variables depicts the state machine forward progress logic. * * keyoff_marker - Enabled when hash byte length needs to be accounted * in field->key_offset update. * field_marker - Enabled when a new field needs to be selected. * group_member - Enabled when protocol is part of a group.
*/
/* Last 4 bits (31:28) are reserved to specify SRC, DST * selection for L3, L4 i.e IPV[4,6]_SRC, IPV[4,6]_DST, * [TCP,UDP,SCTP]_SRC, [TCP,UDP,SCTP]_DST * 31 => L3_SRC, 30 => L3_DST, 29 => L4_SRC, 28 => L4_DST
*/
l3_l4_src_dst = flow_cfg; /* Reset these 4 bits, so that these won't be part of key */
flow_cfg &= NIX_FLOW_KEY_TYPE_L3_L4_MASK;
keyoff_marker = 0; max_key_off = 0; group_member = 0;
nr_field = 0; key_off = 0; field_marker = 1;
field = &tmp; max_bit_pos = fls(flow_cfg); for (idx = 0;
idx < max_bit_pos && nr_field < FIELDS_PER_ALG &&
key_off < MAX_KEY_OFF; idx++) {
key_type = BIT(idx);
valid_key = flow_cfg & key_type; /* Found a field marker, reset the field values */ if (field_marker)
memset(&tmp, 0, sizeof(tmp));
field_marker = true;
keyoff_marker = true; switch (key_type) { case NIX_FLOW_KEY_TYPE_PORT:
field->sel_chan = true; /* This should be set to 1, when SEL_CHAN is set */
field->bytesm1 = 1; break; case NIX_FLOW_KEY_TYPE_IPV4_PROTO:
field->lid = NPC_LID_LC;
field->hdr_offset = 9; /* offset */
field->bytesm1 = 0; /* 1 byte */
field->ltype_match = NPC_LT_LC_IP;
field->ltype_mask = NPC_LT_LC_IP_MATCH_MSK; break; case NIX_FLOW_KEY_TYPE_IPV4: case NIX_FLOW_KEY_TYPE_INNR_IPV4:
field->lid = NPC_LID_LC;
field->ltype_match = NPC_LT_LC_IP; if (key_type == NIX_FLOW_KEY_TYPE_INNR_IPV4) {
field->lid = NPC_LID_LG;
field->ltype_match = NPC_LT_LG_TU_IP;
}
field->hdr_offset = 12; /* SIP offset */
field->bytesm1 = 7; /* SIP + DIP, 8 bytes */
/* Only SIP */ if (l3_l4_src_dst & NIX_FLOW_KEY_TYPE_L3_SRC_ONLY)
field->bytesm1 = 3; /* SIP, 4 bytes */
if (l3_l4_src_dst & NIX_FLOW_KEY_TYPE_L4_DST_ONLY) { /* Both SRC + DST */ if (field->bytesm1 == 1) { /* SRC + DST, 4 bytes */
field->bytesm1 = 3;
} else { /* Only DIP */
field->hdr_offset = 2; /* DST off */
field->bytesm1 = 1; /* DST, 2 bytes */
}
}
/* Enum values for NPC_LID_LD and NPC_LID_LG are same, * so no need to change the ltype_match, just change * the lid for inner protocols
*/
BUILD_BUG_ON((int)NPC_LT_LD_TCP !=
(int)NPC_LT_LH_TU_TCP);
BUILD_BUG_ON((int)NPC_LT_LD_UDP !=
(int)NPC_LT_LH_TU_UDP);
BUILD_BUG_ON((int)NPC_LT_LD_SCTP !=
(int)NPC_LT_LH_TU_SCTP);
if ((key_type == NIX_FLOW_KEY_TYPE_TCP ||
key_type == NIX_FLOW_KEY_TYPE_INNR_TCP) &&
valid_key) {
field->ltype_match |= NPC_LT_LD_TCP;
group_member = true;
} elseif ((key_type == NIX_FLOW_KEY_TYPE_UDP ||
key_type == NIX_FLOW_KEY_TYPE_INNR_UDP) &&
valid_key) {
field->ltype_match |= NPC_LT_LD_UDP;
group_member = true;
} elseif ((key_type == NIX_FLOW_KEY_TYPE_SCTP ||
key_type == NIX_FLOW_KEY_TYPE_INNR_SCTP) &&
valid_key) {
field->ltype_match |= NPC_LT_LD_SCTP;
group_member = true;
}
field->ltype_mask = ~field->ltype_match; if (key_type == NIX_FLOW_KEY_TYPE_SCTP ||
key_type == NIX_FLOW_KEY_TYPE_INNR_SCTP) { /* Handle the case where any of the group item * is enabled in the group but not the final one
*/ if (group_member) {
valid_key = true;
group_member = false;
}
} else {
field_marker = false;
keyoff_marker = false;
}
/* TCP/UDP/SCTP and ESP/AH falls at same offset so * remember the TCP key offset of 40 byte hash key.
*/ if (key_type == NIX_FLOW_KEY_TYPE_TCP)
l4_key_offset = key_off; break; case NIX_FLOW_KEY_TYPE_NVGRE:
field->lid = NPC_LID_LD;
field->hdr_offset = 4; /* VSID offset */
field->bytesm1 = 2;
field->ltype_match = NPC_LT_LD_NVGRE;
field->ltype_mask = 0xF; break; case NIX_FLOW_KEY_TYPE_VXLAN: case NIX_FLOW_KEY_TYPE_GENEVE:
field->lid = NPC_LID_LE;
field->bytesm1 = 2;
field->hdr_offset = 4;
field->ltype_mask = 0xF;
field_marker = false;
keyoff_marker = false;
if (!req->sdp_link && req->maxlen > max_mtu) return NIX_AF_ERR_FRS_INVALID;
if (req->update_minlen && req->minlen < NIC_HW_MIN_FRS) return NIX_AF_ERR_FRS_INVALID;
/* Check if config is for SDP link */ if (req->sdp_link) { if (!hw->sdp_links) return NIX_AF_ERR_RX_LINK_INVALID;
link = hw->cgx_links + hw->lbk_links; goto linkcfg;
}
/* Check if the request is from CGX mapped RVU PF */ if (is_pf_cgxmapped(rvu, pf)) { /* Get CGX and LMAC to which this PF is mapped and find link */
rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx, &lmac);
link = (cgx * hw->lmac_per_cgx) + lmac;
} elseif (pf == 0) { /* For VFs of PF0 ingress is LBK port, so config LBK link */
pfvf = rvu_get_pfvf(rvu, pcifunc);
link = hw->cgx_links + pfvf->lbkid;
} elseif (is_rep_dev(rvu, pcifunc)) {
link = hw->cgx_links + 0;
}
/* Set SDP link credit */
rvu_write64(rvu, blkaddr, NIX_AF_SDP_LINK_CREDIT, SDP_LINK_CREDIT);
/* Set default min/max packet lengths allowed on NIX Rx links. * * With HW reset minlen value of 60byte, HW will treat ARP pkts * as undersize and report them to SW as error pkts, hence * setting it to 40 bytes.
*/ for (link = 0; link < hw->cgx_links; link++) {
rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link),
((u64)lmac_max_frs << 16) | NIC_HW_MIN_FRS);
}
for (link = hw->cgx_links; link < hw->lbk_links; link++) {
rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link),
((u64)lbk_max_frs << 16) | NIC_HW_MIN_FRS);
} if (hw->sdp_links) {
link = hw->cgx_links + hw->lbk_links;
rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link),
SDP_HW_MAX_FRS << 16 | SDP_HW_MIN_FRS);
}
/* Get MCS external bypass status for CN10K-B */ if (mcs_get_blkcnt() == 1) { /* Adjust for 2 credits when external bypass is disabled */
nix_hw->cc_mcs_cnt = is_mcs_bypass(0) ? 0 : 2;
}
/* Set credits for Tx links assuming max packet length allowed. * This will be reconfigured based on MTU set for PF/VF.
*/ for (cgx = 0; cgx < hw->cgx; cgx++) {
lmac_cnt = cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu)); /* Skip when cgx is not available or lmac cnt is zero */ if (lmac_cnt <= 0) continue;
slink = cgx * hw->lmac_per_cgx;
/* Get LMAC id's from bitmap */
lmac_bmap = cgx_get_lmac_bmap(rvu_cgx_pdata(cgx, rvu));
for_each_set_bit(iter, &lmac_bmap, rvu->hw->lmac_per_cgx) {
lmac_fifo_len = rvu_cgx_get_lmac_fifolen(rvu, cgx, iter); if (!lmac_fifo_len) {
dev_err(rvu->dev, "%s: Failed to get CGX/RPM%d:LMAC%d FIFO size\n",
__func__, cgx, iter); continue;
}
tx_credits = (lmac_fifo_len - lmac_max_frs) / 16; /* Enable credits and set credit pkt count to max allowed */
cfg = (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
cfg |= FIELD_PREP(NIX_AF_LINKX_MCS_CNT_MASK, nix_hw->cc_mcs_cnt);
link = iter + slink;
nix_hw->tx_credits[link] = tx_credits;
rvu_write64(rvu, blkaddr,
NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg);
}
}
/* Set Tx credits for LBK link */
slink = hw->cgx_links; for (link = slink; link < (slink + hw->lbk_links); link++) {
tx_credits = rvu_get_lbk_link_credits(rvu, lbk_max_frs);
nix_hw->tx_credits[link] = tx_credits; /* Enable credits and set credit pkt count to max allowed */
tx_credits = (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
rvu_write64(rvu, blkaddr,
NIX_AF_TX_LINKX_NORM_CREDIT(link), tx_credits);
}
}
staticint nix_calibrate_x2p(struct rvu *rvu, int blkaddr)
{ int idx, err;
u64 status;
/* Start X2P bus calibration */
rvu_write64(rvu, blkaddr, NIX_AF_CFG,
rvu_read64(rvu, blkaddr, NIX_AF_CFG) | BIT_ULL(9)); /* Wait for calibration to complete */
err = rvu_poll_reg(rvu, blkaddr,
NIX_AF_STATUS, BIT_ULL(10), false); if (err) {
dev_err(rvu->dev, "NIX X2P bus calibration failed\n"); return err;
}
status = rvu_read64(rvu, blkaddr, NIX_AF_STATUS); /* Check if CGX devices are ready */ for (idx = 0; idx < rvu->cgx_cnt_max; idx++) { /* Skip when cgx port is not available */ if (!rvu_cgx_pdata(idx, rvu) ||
(status & (BIT_ULL(16 + idx)))) continue;
dev_err(rvu->dev, "CGX%d didn't respond to NIX X2P calibration\n", idx);
err = -EBUSY;
}
/* Check if LBK is ready */ if (!(status & BIT_ULL(19))) {
dev_err(rvu->dev, "LBK didn't respond to NIX X2P calibration\n");
err = -EBUSY;
}
/* Clear 'calibrate_x2p' bit */
rvu_write64(rvu, blkaddr, NIX_AF_CFG,
rvu_read64(rvu, blkaddr, NIX_AF_CFG) & ~BIT_ULL(9)); if (err || (status & 0x3FFULL))
dev_err(rvu->dev, "NIX X2P calibration failed, status 0x%llx\n", status); if (err) return err; return 0;
}
/* Result structure can be followed by RQ/SQ/CQ context at * RES + 128bytes and a write mask at RES + 256 bytes, depending on * operation type. Alloc sufficient result memory for all operations.
*/
err = rvu_aq_alloc(rvu, &block->aq,
Q_COUNT(AQ_SIZE), sizeof(struct nix_aq_inst_s),
ALIGN(sizeof(struct nix_aq_res_s), 128) + 256); if (err) return err;
/* On OcteonTx2 DWRR quantum is directly configured into each of * the transmit scheduler queues. And PF/VF drivers were free to * config any value upto 2^24. * On CN10K, HW is modified, the quantum configuration at scheduler * queues is in terms of weight. And SW needs to setup a base DWRR MTU * at NIX_AF_DWRR_RPM_MTU / NIX_AF_DWRR_SDP_MTU. HW will do * 'DWRR MTU * weight' to get the quantum. * * Check if HW uses a common MTU for all DWRR quantum configs. * On OcteonTx2 this register field is '0'.
*/ if ((((hw_const >> 56) & 0x10) == 0x10) && !(hw_const & BIT_ULL(61)))
hw->cap.nix_common_dwrr_mtu = true;
if (hw_const & BIT_ULL(61))
hw->cap.nix_multiple_dwrr_mtu = true;
}
if (is_rvu_96xx_B0(rvu)) { /* As per a HW errata in 96xx A0/B0 silicon, NIX may corrupt * internal state when conditional clocks are turned off. * Hence enable them.
*/
rvu_write64(rvu, blkaddr, NIX_AF_CFG,
rvu_read64(rvu, blkaddr, NIX_AF_CFG) | 0x40ULL);
}
/* Set chan/link to backpressure TL3 instead of TL2 */
rvu_write64(rvu, blkaddr, NIX_AF_PSE_CHANNEL_LEVEL, 0x01);
/* Disable SQ manager's sticky mode operation (set TM6 = 0) * This sticky mode is known to cause SQ stalls when multiple * SQs are mapped to same SMQ and transmitting pkts at a time.
*/
cfg = rvu_read64(rvu, blkaddr, NIX_AF_SQM_DBG_CTL_STATUS);
cfg &= ~BIT_ULL(15);
rvu_write64(rvu, blkaddr, NIX_AF_SQM_DBG_CTL_STATUS, cfg);
ltdefs = rvu->kpu.lt_def; /* Calibrate X2P bus to check if CGX/LBK links are fine */
err = nix_calibrate_x2p(rvu, blkaddr); if (err) return err;
/* Setup capabilities of the NIX block */
rvu_nix_setup_capabilities(rvu, blkaddr);
/* Iterate the group elements and disable the element which * received the disable request.
*/
mce_list = &elem->mcast_mce_list;
hlist_for_each_entry(mce, &mce_list->head, node) { if (mce->pcifunc == pcifunc) {
mce->is_active = is_active; break;
}
}
/* Dump the updated list to HW */ if (elem->dir == NIX_MCAST_INGRESS)
nix_update_ingress_mce_list_hw(rvu, nix_hw, elem); else
nix_update_egress_mce_list_hw(rvu, nix_hw, elem);
/* Update the multicast index in NPC rule */
nix_mcast_update_action(rvu, elem);
}
mutex_unlock(&mcast_grp->mcast_grp_lock);
}
err = nix_get_nixlf(rvu, pcifunc, &nixlf, NULL); if (err) return err;
rvu_npc_disable_mcam_entries(rvu, pcifunc, nixlf); /* Disable the interface if it is in any multicast list */
nix_mcast_update_mce_entry(rvu, pcifunc, 0);
cpt_idx = (blkaddr == BLKADDR_NIX0) ? 0 : 1; if (req->enable) {
val = 0; /* Enable context prefetching */ if (!is_rvu_otx2(rvu))
val |= BIT_ULL(51);
/* Set OPCODE and EGRP */
val |= FIELD_PREP(IPSEC_GEN_CFG_EGRP, req->gen_cfg.egrp);
val |= FIELD_PREP(IPSEC_GEN_CFG_OPCODE, req->gen_cfg.opcode);
val |= FIELD_PREP(IPSEC_GEN_CFG_PARAM1, req->gen_cfg.param1);
val |= FIELD_PREP(IPSEC_GEN_CFG_PARAM2, req->gen_cfg.param2);
/* Set CPT queue for inline IPSec */
val = FIELD_PREP(CPT_INST_QSEL_SLOT, req->inst_qsel.cpt_slot);
val |= FIELD_PREP(CPT_INST_QSEL_PF_FUNC,
req->inst_qsel.cpt_pf_func);
if (!is_rvu_otx2(rvu)) {
cpt_blkaddr = (cpt_idx == 0) ? BLKADDR_CPT0 :
BLKADDR_CPT1;
val |= FIELD_PREP(CPT_INST_QSEL_BLOCK, cpt_blkaddr);
}
/* Extract PCP and DEI fields from outer VLAN from byte offset * 2 from the start of LB_PTR (ie TAG). * VLAN0 is Outer VLAN and VLAN1 is Inner VLAN. Inner VLAN * fields are considered when 'Tunnel enable' is set in profile.
*/
rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_VLAN0_PCP_DEI,
(2UL << 12) | (ltdefs->ovlan.lid << 8) |
(ltdefs->ovlan.ltype_match << 4) |
ltdefs->ovlan.ltype_mask);
rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_VLAN1_PCP_DEI,
(2UL << 12) | (ltdefs->ivlan.lid << 8) |
(ltdefs->ivlan.ltype_match << 4) |
ltdefs->ivlan.ltype_mask);
err = rvu_alloc_bitmap(&ipolicer->band_prof); if (err) return err;
ipolicer->pfvf_map = devm_kcalloc(rvu->dev,
ipolicer->band_prof.max, sizeof(u16), GFP_KERNEL); if (!ipolicer->pfvf_map) return -ENOMEM;
ipolicer->match_id = devm_kcalloc(rvu->dev,
ipolicer->band_prof.max, sizeof(u16), GFP_KERNEL); if (!ipolicer->match_id) return -ENOMEM;
for (prof_idx = 0;
prof_idx < ipolicer->band_prof.max; prof_idx++) { /* Set AF as current owner for INIT ops to succeed */
ipolicer->pfvf_map[prof_idx] = 0x00;
/* There is no enable bit in the profile context, * so no context disable. So let's INIT them here * so that PF/VF later on have to just do WRITE to * setup policer rates and config.
*/
err = nix_init_policer_context(rvu, nix_hw,
layer, prof_idx); if (err) return err;
}
/* Allocate memory for maintaining ref_counts for MID level * profiles, this will be needed for leaf layer profiles' * aggregation.
*/ if (layer != BAND_PROF_MID_LAYER) continue;
ipolicer = &nix_hw->ipolicer[layer]; if (prof_idx >= ipolicer->band_prof.max) return -EINVAL;
/* Check if the profile is allocated to the requesting PCIFUNC or not * with the exception of AF. AF is allowed to read and update contexts.
*/ if (pcifunc && ipolicer->pfvf_map[prof_idx] != pcifunc) return -EINVAL;
/* If this profile is linked to higher layer profile then check * if that profile is also allocated to the requesting PCIFUNC * or not.
*/ if (!req->prof.hl_en) return 0;
/* Leaf layer profile can link only to mid layer and * mid layer to top layer.
*/ if (layer == BAND_PROF_LEAF_LAYER)
hi_layer = BAND_PROF_MID_LAYER; elseif (layer == BAND_PROF_MID_LAYER)
hi_layer = BAND_PROF_TOP_LAYER; else return -EINVAL;
if (!rvu->hw->cap.ipolicer) return NIX_AF_ERR_IPOLICER_NOTSUPP;
err = nix_get_struct_ptrs(rvu, pcifunc, &nix_hw, &blkaddr); if (err) return err;
mutex_lock(&rvu->rsrc_lock); for (layer = 0; layer < BAND_PROF_NUM_LAYERS; layer++) { if (layer == BAND_PROF_INVAL_LAYER) continue; if (!req->prof_count[layer]) continue;
ipolicer = &nix_hw->ipolicer[layer]; for (idx = 0; idx < req->prof_count[layer]; idx++) { /* Allocate a max of 'MAX_BANDPROF_PER_PFFUNC' profiles */ if (idx == MAX_BANDPROF_PER_PFFUNC) break;
if (!rvu->hw->cap.ipolicer) return NIX_AF_ERR_IPOLICER_NOTSUPP;
err = nix_get_struct_ptrs(rvu, pcifunc, &nix_hw, &blkaddr); if (err) return err;
mutex_lock(&rvu->rsrc_lock); /* Free all the profiles allocated to the PCIFUNC */ for (layer = 0; layer < BAND_PROF_NUM_LAYERS; layer++) { if (layer == BAND_PROF_INVAL_LAYER) continue;
ipolicer = &nix_hw->ipolicer[layer];
for (prof_idx = 0; prof_idx < ipolicer->band_prof.max; prof_idx++) { if (ipolicer->pfvf_map[prof_idx] != pcifunc) continue;
/* Clear ratelimit aggregation, if any */ if (layer == BAND_PROF_LEAF_LAYER &&
ipolicer->match_id[prof_idx])
nix_clear_ratelimit_aggr(rvu, nix_hw, prof_idx);
if (req->free_all) return nix_free_all_bandprof(rvu, pcifunc);
if (!rvu->hw->cap.ipolicer) return NIX_AF_ERR_IPOLICER_NOTSUPP;
err = nix_get_struct_ptrs(rvu, pcifunc, &nix_hw, &blkaddr); if (err) return err;
mutex_lock(&rvu->rsrc_lock); /* Free the requested profile indices */ for (layer = 0; layer < BAND_PROF_NUM_LAYERS; layer++) { if (layer == BAND_PROF_INVAL_LAYER) continue; if (!req->prof_count[layer]) continue;
ipolicer = &nix_hw->ipolicer[layer]; for (idx = 0; idx < req->prof_count[layer]; idx++) { if (idx == MAX_BANDPROF_PER_PFFUNC) break;
prof_idx = req->prof_idx[layer][idx]; if (prof_idx >= ipolicer->band_prof.max ||
ipolicer->pfvf_map[prof_idx] != pcifunc) continue;
/* Clear ratelimit aggregation, if any */ if (layer == BAND_PROF_LEAF_LAYER &&
ipolicer->match_id[prof_idx])
nix_clear_ratelimit_aggr(rvu, nix_hw, prof_idx);
rc = nix_get_struct_ptrs(rvu, pcifunc, &nix_hw, &blkaddr); if (rc) return rc;
/* Fetch the RQ's context to see if policing is enabled */
rc = nix_aq_context_read(rvu, nix_hw, &aq_req, &aq_rsp, pcifunc,
NIX_AQ_CTYPE_RQ, rq_idx); if (rc) {
dev_err(rvu->dev, "%s: Failed to fetch RQ%d context of PFFUNC 0x%x\n",
__func__, rq_idx, pcifunc); return rc;
}
if (!aq_rsp.rq.policer_ena) return 0;
/* Get the bandwidth profile ID mapped to this RQ */
leaf_prof = aq_rsp.rq.band_prof_id;
/* Check if any other leaf profile is marked with same match_id */ for (idx = 0; idx < ipolicer->band_prof.max; idx++) { if (idx == leaf_prof) continue; if (ipolicer->match_id[idx] != match_id) continue;
leaf_match = idx; break;
}
if (idx == ipolicer->band_prof.max) return 0;
/* Fetch the matching profile's context to check if it's already * mapped to a mid level profile.
*/
rc = nix_aq_context_read(rvu, nix_hw, &aq_req, &aq_rsp, 0x00,
NIX_AQ_CTYPE_BANDPROF, leaf_match); if (rc) {
dev_err(rvu->dev, "%s: Failed to fetch context of leaf profile %d\n",
__func__, leaf_match); return rc;
}
ipolicer = &nix_hw->ipolicer[BAND_PROF_MID_LAYER]; if (aq_rsp.prof.hl_en) { /* Get Mid layer prof index and map leaf_prof index * also such that flows that are being steered * to different RQs and marked with same match_id * are rate limited in a aggregate fashion
*/
mid_prof = aq_rsp.prof.band_prof_id;
rc = nix_ipolicer_map_leaf_midprofs(rvu, nix_hw,
&aq_req, &aq_rsp,
leaf_prof, mid_prof); if (rc) {
dev_err(rvu->dev, "%s: Failed to map leaf(%d) and mid(%d) profiles\n",
__func__, leaf_prof, mid_prof); gotoexit;
}
if (!rvu->hw->cap.ipolicer) return NIX_AF_ERR_IPOLICER_NOTSUPP;
err = nix_get_struct_ptrs(rvu, req->hdr.pcifunc, &nix_hw, &blkaddr); if (err) return err;
/* Return number of bandwidth profiles free at each layer */
mutex_lock(&rvu->rsrc_lock); for (layer = 0; layer < BAND_PROF_NUM_LAYERS; layer++) { if (layer == BAND_PROF_INVAL_LAYER) continue;
/* If the pcifunc which created the multicast/mirror * group received an FLR, then delete the entire group.
*/ if (elem->pcifunc == pcifunc) { /* Delete group */
dreq.hdr.pcifunc = elem->pcifunc;
dreq.mcast_grp_idx = elem->mcast_grp_idx;
dreq.is_af = 1;
rvu_mbox_handler_nix_mcast_grp_destroy(rvu, &dreq, NULL); continue;
}
/* Iterate the group elements and delete the element which * received the FLR.
*/
mce_list = &elem->mcast_mce_list;
hlist_for_each_entry_safe(mce, tmp, &mce_list->head, node) { if (mce->pcifunc == pcifunc) {
ureq.hdr.pcifunc = pcifunc;
ureq.num_mce_entry = 1;
ureq.mcast_grp_idx = elem->mcast_grp_idx;
ureq.op = NIX_MCAST_OP_DEL_ENTRY;
ureq.pcifunc[0] = pcifunc;
ureq.is_af = 1;
rvu_mbox_handler_nix_mcast_grp_update(rvu, &ureq, &ursp); break;
}
}
}
mutex_unlock(&mcast_grp->mcast_grp_lock);
}
int rvu_nix_mcast_update_mcam_entry(struct rvu *rvu, u16 pcifunc,
u32 mcast_grp_idx, u16 mcam_index)
{ struct nix_mcast_grp_elem *elem; struct nix_mcast_grp *mcast_grp; struct nix_hw *nix_hw; int blkaddr, ret = 0;
err = nix_get_struct_ptrs(rvu, req->hdr.pcifunc, &nix_hw, &blkaddr); if (err) return err;
mcast_grp = &nix_hw->mcast_grp;
/* If AF is requesting for the deletion, * then AF is already taking the lock
*/ if (!req->is_af)
mutex_lock(&mcast_grp->mcast_grp_lock);
elem = rvu_nix_mcast_find_grp_elem(mcast_grp, req->mcast_grp_idx); if (!elem) {
ret = NIX_AF_ERR_INVALID_MCAST_GRP; goto unlock_grp;
}
/* If no mce entries are associated with the group * then just remove it from the global list.
*/ if (!elem->mcast_mce_list.count) goto delete_grp;
/* Delete the associated mcam entry and * remove all mce entries from the group
*/
mcast = &nix_hw->mcast;
mutex_lock(&mcast->mce_lock); if (elem->mcam_index != -1) {
uninstall_req.hdr.pcifunc = req->hdr.pcifunc;
uninstall_req.entry = elem->mcam_index;
rvu_mbox_handler_npc_delete_flow(rvu, &uninstall_req, &uninstall_rsp);
}
err = nix_get_struct_ptrs(rvu, req->hdr.pcifunc, &nix_hw, &blkaddr); if (err) return err;
mcast_grp = &nix_hw->mcast_grp;
/* If AF is requesting for the updation, * then AF is already taking the lock
*/ if (!req->is_af)
mutex_lock(&mcast_grp->mcast_grp_lock);
elem = rvu_nix_mcast_find_grp_elem(mcast_grp, req->mcast_grp_idx); if (!elem) {
ret = NIX_AF_ERR_INVALID_MCAST_GRP; goto unlock_grp;
}
/* If any pcifunc matches the group's pcifunc, then we can * delete the entire group.
*/ if (req->op == NIX_MCAST_OP_DEL_ENTRY) { for (i = 0; i < req->num_mce_entry; i++) { if (elem->pcifunc == req->pcifunc[i]) { /* Delete group */
dreq.hdr.pcifunc = elem->pcifunc;
dreq.mcast_grp_idx = elem->mcast_grp_idx;
dreq.is_af = 1;
rvu_mbox_handler_nix_mcast_grp_destroy(rvu, &dreq, NULL);
ret = 0; goto unlock_grp;
}
}
}
/* It is possible not to get contiguous memory */ if (elem->mce_start_index < 0) { if (elem->mcam_index != -1) {
npc_enable_mcam_entry(rvu, mcam, npc_blkaddr,
elem->mcam_index, true);
ret = NIX_AF_ERR_NON_CONTIG_MCE_LIST; goto unlock_mce;
}
}
ret = nix_add_mce_list_entry(rvu, nix_hw, elem, req); if (ret) {
nix_free_mce_list(mcast, new_count, elem->mce_start_index, elem->dir); if (prev_count)
elem->mce_start_index = nix_alloc_mce_list(mcast,
prev_count,
elem->dir);
if (elem->mcam_index != -1)
npc_enable_mcam_entry(rvu, mcam, npc_blkaddr,
elem->mcam_index, true);
goto unlock_mce;
}
} else { if (!prev_count || prev_count < req->num_mce_entry) { if (elem->mcam_index != -1)
npc_enable_mcam_entry(rvu, mcam, npc_blkaddr,
elem->mcam_index, true);
ret = NIX_AF_ERR_INVALID_MCAST_DEL_REQ; goto unlock_mce;
}
unlock_grp: if (!req->is_af)
mutex_unlock(&mcast_grp->mcast_grp_lock);
return ret;
}
/* On CN10k and older series of silicons, hardware may incorrectly * assert XOFF on certain channels. Issue a write on NIX_AF_RX_CHANX_CFG * to broadcacst XON on the same.
*/ void rvu_block_bcast_xon(struct rvu *rvu, int blkaddr)
{ struct rvu_block *block = &rvu->hw->block[blkaddr];
u64 cfg;
if (!block->implemented || is_cn20k(rvu->pdev)) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.