* [PATCH v2 net-next] octeontx2-af: Add couple of mailbox support.
@ 2026-09-24 2:44 Ratheesh Kannoth
2026-09-28 3:01 ` netdev-bot+sashiko
0 siblings, 1 reply; 4+ messages in thread
From: Ratheesh Kannoth @ 2026-09-24 2:44 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham, Ratheesh Kannoth
Add IFACE_GET_INFO and NPC batch flow/stats/features handlers for the
RVU PFs, with synchronized NIX context and channel publishing.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
v1 -> v2: Addressed sashiko comments.
- Set pfvf->lbkid before nix_interface_set_chan() in the LBK init path,
so IFACE_GET_INFO cannot see tx_chan_cnt under rsrc_lock and then read
a stale lbkid from nix_get_tx_link().
- Null-initialize cq_ints_ctx before qmem_alloc(), so the error path does
not pass an uninitialized pointer to qmem_free() when hardware reports
a zero CQINT count.
- Null-initialize nix_qints_ctx before qmem_alloc() for the same QINT
count zero case.
https://lore.kernel.org/netdev/20260918041142.1342830-1-rkannoth@marvell.com/
---
.../net/ethernet/marvell/octeontx2/af/mbox.h | 62 ++++
.../net/ethernet/marvell/octeontx2/af/rvu.c | 159 ++++++++++
.../net/ethernet/marvell/octeontx2/af/rvu.h | 2 +
.../ethernet/marvell/octeontx2/af/rvu_nix.c | 293 ++++++++++++++----
.../ethernet/marvell/octeontx2/af/rvu_npc.c | 124 ++++++++
.../marvell/octeontx2/af/rvu_npc_fs.c | 23 +-
6 files changed, 598 insertions(+), 65 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index cece197d1074..28f2b549780f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -164,6 +164,8 @@ M(PTP_GET_CAP, 0x00c, ptp_get_cap, msg_req, ptp_get_cap_rsp) \
M(GET_REP_CNT, 0x00d, get_rep_cnt, msg_req, get_rep_cnt_rsp) \
M(ESW_CFG, 0x00e, esw_cfg, esw_cfg_req, msg_rsp) \
M(REP_EVENT_NOTIFY, 0x00f, rep_event_notify, rep_event, msg_rsp) \
+M(IFACE_GET_INFO, 0x014, iface_get_info, msg_req, \
+ iface_get_info_rsp) \
/* CGX mbox IDs (range 0x200 - 0x3FF) */ \
M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \
M(CGX_STOP_RXTX, 0x201, cgx_stop_rxtx, msg_req, msg_rsp) \
@@ -309,8 +311,16 @@ M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601e, npc_get_dft_rl_idxs, \
M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601f, npc_get_pfl_info, \
msg_req, \
npc_get_pfl_info_rsp) \
+M(NPC_MCAM_FLOW_DEL_N_FREE, 0x6020, npc_flow_del_n_free, \
+ npc_flow_del_n_free_req, msg_rsp) \
M(NPC_MCAM_READ_DEFAULT_RULE, 0x6021, npc_read_default_rule, msg_req, \
npc_mcam_read_base_rule_rsp) \
+M(NPC_MCAM_GET_MUL_STATS, 0x6022, npc_mcam_mul_stats, \
+ npc_mcam_get_mul_stats_req, \
+ npc_mcam_get_mul_stats_rsp) \
+M(NPC_MCAM_GET_FEATURES, 0x6023, npc_mcam_get_features, \
+ msg_req, \
+ npc_mcam_get_features_rsp) \
/* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \
M(NIX_LF_ALLOC, 0x8000, nix_lf_alloc, \
nix_lf_alloc_req, nix_lf_alloc_rsp) \
@@ -1586,6 +1596,31 @@ struct npc_mcam_alloc_entry_rsp {
u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];
};
+struct npc_flow_del_n_free_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 entry[256]; /* Entry index to be freed */
+};
+
+struct npc_mcam_get_features_rsp {
+ struct mbox_msghdr hdr;
+ u64 rx_features;
+ u64 tx_features;
+};
+
+struct npc_mcam_get_mul_stats_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 entry[256]; /* mcam entry */
+};
+
+struct npc_mcam_get_mul_stats_rsp {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 rsvd[3]; /* explicit padding for stat[] 8-byte alignment */
+ u64 stat[256]; /* counter stats */
+};
+
struct npc_mcam_free_entry_req {
struct mbox_msghdr hdr;
u16 entry; /* Entry index to be freed */
@@ -1812,6 +1847,33 @@ struct rep_event {
struct rep_evt_data evt_data;
};
+struct iface_info {
+ u8 is_vf : 1;
+ u8 is_sdp : 1;
+ u8 rsvd : 6;
+ u16 pcifunc;
+ u16 rx_chan_base;
+ u16 tx_chan_base;
+ u16 sq_cnt;
+ u16 cq_cnt;
+ u16 rq_cnt;
+ u8 rx_chan_cnt;
+ u8 tx_chan_cnt;
+ u8 tx_link;
+ u8 nix;
+};
+
+/* Max supported */
+#define IFACE_MAX (256 + 32) /* 32 PFs + 256 VFs */
+
+struct iface_get_info_rsp {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u8 truncated;
+ u8 rsvd[5];
+ struct iface_info info[IFACE_MAX];
+};
+
struct flow_msg {
unsigned char dmac[6];
unsigned char smac[6];
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index 30e148291581..055e39746558 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -1990,6 +1990,165 @@ int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,
return 0;
}
+static bool rvu_is_switch_pcifunc(struct rvu *rvu, u16 pcifunc)
+{
+ int pf, domain_nr;
+ struct pci_dev *pdev;
+ bool is_switch;
+
+ if (pcifunc & RVU_PFVF_FUNC_MASK)
+ return false;
+
+ pf = rvu_get_pf(rvu->pdev, pcifunc);
+ domain_nr = pci_domain_nr(rvu->pdev->bus);
+ pdev = pci_get_domain_bus_and_slot(domain_nr,
+ pf + rvu->pdev->bus->number, 0);
+ if (!pdev)
+ return false;
+
+ is_switch = pdev->device == PCI_DEVID_PAN_RVU;
+ pci_dev_put(pdev);
+
+ return is_switch;
+}
+
+static void rvu_iface_get_qcnts(struct rvu *rvu, struct rvu_pfvf *pfvf,
+ struct iface_info *info)
+{
+ struct admin_queue *aq;
+
+ info->sq_cnt = 0;
+ info->cq_cnt = 0;
+ info->rq_cnt = 0;
+
+ aq = rvu->hw->block[pfvf->nix_blkaddr].aq;
+ if (!aq)
+ return;
+
+ spin_lock(&aq->lock);
+
+ /* Use each LF queue context size; bitmaps are sized to qsize longs. */
+ if (pfvf->sq_ctx && pfvf->sq_bmap)
+ info->sq_cnt = bitmap_weight(pfvf->sq_bmap, pfvf->sq_ctx->qsize);
+ if (pfvf->cq_ctx && pfvf->cq_bmap)
+ info->cq_cnt = bitmap_weight(pfvf->cq_bmap, pfvf->cq_ctx->qsize);
+ if (pfvf->rq_ctx && pfvf->rq_bmap)
+ info->rq_cnt = bitmap_weight(pfvf->rq_bmap, pfvf->rq_ctx->qsize);
+
+ spin_unlock(&aq->lock);
+}
+
+int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req,
+ struct iface_get_info_rsp *rsp)
+{
+ struct iface_info *info;
+ bool truncated = false;
+ struct rvu_pfvf *pfvf;
+ int pf, vf, numvfs;
+ int tot = 0;
+ u16 pcifunc;
+ u64 cfg;
+
+ if (!rvu_is_switch_pcifunc(rvu, req->hdr.pcifunc))
+ return -EPERM;
+
+ /* Read-only topology snapshot for switch software. */
+ rsp->cnt = 0;
+ rsp->truncated = 0;
+ memset(rsp->rsvd, 0, sizeof(rsp->rsvd));
+ /* Preserve mbox_msghdr fields pre-filled by the mbox framework. */
+ memset(rsp->info, 0, sizeof(rsp->info));
+ info = rsp->info;
+
+ mutex_lock(&rvu->rsrc_lock);
+ for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+ if (tot >= IFACE_MAX) {
+ truncated = true;
+ goto done;
+ }
+
+ cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+ numvfs = (cfg >> 12) & 0xFF;
+
+ /* Skip not enabled PFs */
+ if (!(cfg & BIT_ULL(20)))
+ goto chk_vfs;
+
+ /* If Admin function, check on VFs */
+ if (cfg & BIT_ULL(21))
+ goto chk_vfs;
+
+ pcifunc = rvu_make_pcifunc(rvu->pdev, pf, 0);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+ /* Populate iff at least one Tx channel */
+ if (!pfvf->tx_chan_cnt)
+ goto chk_vfs;
+
+ info->is_vf = 0;
+ info->pcifunc = pcifunc;
+ info->rx_chan_base = pfvf->rx_chan_base;
+ info->rx_chan_cnt = pfvf->rx_chan_cnt;
+ info->tx_chan_base = pfvf->tx_chan_base;
+ info->tx_chan_cnt = pfvf->tx_chan_cnt;
+ info->tx_link = nix_get_tx_link(rvu, pcifunc);
+ if (is_sdp_pfvf(rvu, pcifunc))
+ info->is_sdp = 1;
+
+ rvu_iface_get_qcnts(rvu, pfvf, info);
+
+ if (pfvf->nix_blkaddr == BLKADDR_NIX0)
+ info->nix = 0;
+ else
+ info->nix = 1;
+
+ info++;
+ tot++;
+
+chk_vfs:
+ for (vf = 0; vf < numvfs; vf++) {
+ if (tot >= IFACE_MAX) {
+ truncated = true;
+ goto done;
+ }
+
+ pcifunc = rvu_make_pcifunc(rvu->pdev, pf, vf + 1);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+ if (!pfvf->tx_chan_cnt)
+ continue;
+
+ info->is_vf = 1;
+ info->pcifunc = pcifunc;
+ info->rx_chan_base = pfvf->rx_chan_base;
+ info->rx_chan_cnt = pfvf->rx_chan_cnt;
+ info->tx_chan_base = pfvf->tx_chan_base;
+ info->tx_chan_cnt = pfvf->tx_chan_cnt;
+ info->tx_link = nix_get_tx_link(rvu, pcifunc);
+ if (is_sdp_pfvf(rvu, pcifunc))
+ info->is_sdp = 1;
+
+ rvu_iface_get_qcnts(rvu, pfvf, info);
+
+ if (pfvf->nix_blkaddr == BLKADDR_NIX0)
+ info->nix = 0;
+ else
+ info->nix = 1;
+
+ info++;
+
+ tot++;
+ }
+ }
+done:
+ mutex_unlock(&rvu->rsrc_lock);
+
+ rsp->cnt = tot;
+ rsp->truncated = truncated;
+
+ return 0;
+}
+
int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req,
struct free_rsrcs_rsp *rsp)
{
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index fb4870cd18e9..d3408b86f0de 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -24,6 +24,7 @@
#define PCI_DEVID_OCTEONTX2_RVU_AF 0xA065
#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8
#define PCI_DEVID_OCTEONTX2_LBK 0xA061
+#define PCI_DEVID_PAN_RVU 0xA0E2
/* Subsystem Device ID */
#define PCI_SUBSYS_DEVID_98XX 0xB100
@@ -1161,6 +1162,7 @@ void rvu_program_channels(struct rvu *rvu);
/* CN10K NIX */
void rvu_nix_block_cn10k_init(struct rvu *rvu, struct nix_hw *nix_hw);
+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);
/* CN10K RVU - LMT*/
void rvu_reset_lmt_map_tbl(struct rvu *rvu, u16 pcifunc);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 93c5c4b298bf..26836c243c50 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -32,7 +32,6 @@ static int nix_free_all_bandprof(struct rvu *rvu, u16 pcifunc);
static void nix_clear_ratelimit_aggr(struct rvu *rvu, struct nix_hw *nix_hw,
u32 leaf_prof);
static const char *nix_get_ctx_name(int ctype);
-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);
enum mc_tbl_sz {
MC_TBL_SZ_256,
@@ -337,6 +336,28 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
return true;
}
+static void nix_interface_set_chan(struct rvu *rvu, struct rvu_pfvf *pfvf,
+ u16 rx_chan_base, u8 rx_chan_cnt,
+ u16 tx_chan_base, u8 tx_chan_cnt)
+{
+ mutex_lock(&rvu->rsrc_lock);
+ pfvf->rx_chan_base = rx_chan_base;
+ pfvf->rx_chan_cnt = rx_chan_cnt;
+ pfvf->tx_chan_base = tx_chan_base;
+ pfvf->tx_chan_cnt = tx_chan_cnt;
+ mutex_unlock(&rvu->rsrc_lock);
+}
+
+static void nix_interface_clear_chan(struct rvu *rvu, struct rvu_pfvf *pfvf)
+{
+ mutex_lock(&rvu->rsrc_lock);
+ pfvf->rx_chan_base = 0;
+ pfvf->rx_chan_cnt = 0;
+ pfvf->tx_chan_base = 0;
+ pfvf->tx_chan_cnt = 0;
+ mutex_unlock(&rvu->rsrc_lock);
+}
+
static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
struct nix_lf_alloc_rsp *rsp, bool loop)
{
@@ -365,10 +386,9 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
"PF_Func 0x%x: Invalid pkind\n", pcifunc);
return -EINVAL;
}
- pfvf->rx_chan_base = rvu_nix_chan_cgx(rvu, cgx_id, lmac_id, 0);
- pfvf->tx_chan_base = pfvf->rx_chan_base;
- pfvf->rx_chan_cnt = 1;
- pfvf->tx_chan_cnt = 1;
+ nix_interface_set_chan(rvu, pfvf,
+ rvu_nix_chan_cgx(rvu, cgx_id, lmac_id, 0), 1,
+ rvu_nix_chan_cgx(rvu, cgx_id, lmac_id, 0), 1);
rsp->tx_link = cgx_id * hw->lmac_per_cgx + lmac_id;
if (rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, pkind))
@@ -410,14 +430,13 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
* 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;
+ nix_interface_set_chan(rvu, pfvf,
+ rvu_nix_chan_lbk(rvu, lbkid, vf), 1,
+ vf & 0x1 ?
+ rvu_nix_chan_lbk(rvu, lbkid, vf - 1) :
+ rvu_nix_chan_lbk(rvu, lbkid, vf + 1), 1);
+ rsp->tx_link = hw->cgx_links + lbkid;
rvu_npc_set_pkind(rvu, NPC_RX_LBK_PKIND, pfvf);
rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
pfvf->rx_chan_base,
@@ -452,10 +471,8 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
req_chan_cnt = sdp_info->num_pf_rings;
}
- pfvf->rx_chan_base = req_chan_base;
- pfvf->rx_chan_cnt = req_chan_cnt;
- pfvf->tx_chan_base = pfvf->rx_chan_base;
- pfvf->tx_chan_cnt = pfvf->rx_chan_cnt;
+ nix_interface_set_chan(rvu, pfvf, req_chan_base, req_chan_cnt,
+ req_chan_base, req_chan_cnt);
rsp->tx_link = hw->cgx_links + hw->lbk_links;
rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
@@ -912,33 +929,114 @@ static void nix_setup_lso(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
nix_hw->lso.in_use++;
}
+static int nix_qctx_assign(struct rvu *rvu, int blkaddr, struct qmem **ctx,
+ unsigned long **bmap, struct qmem *new_ctx,
+ unsigned long *new_bmap)
+{
+ struct admin_queue *aq = rvu->hw->block[blkaddr].aq;
+ unsigned long flags;
+
+ if (!aq) {
+ WARN_ON_ONCE(1);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&aq->lock, flags);
+ *ctx = new_ctx;
+ *bmap = new_bmap;
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ return 0;
+}
+
+static int nix_ctx_assign(struct rvu *rvu, int blkaddr, struct qmem **ctx,
+ struct qmem *new_ctx)
+{
+ struct admin_queue *aq = rvu->hw->block[blkaddr].aq;
+ unsigned long flags;
+
+ if (!aq) {
+ WARN_ON_ONCE(1);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&aq->lock, flags);
+ *ctx = new_ctx;
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ return 0;
+}
+
static void 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);
+ struct admin_queue *aq = rvu->hw->block[pfvf->nix_blkaddr].aq;
+ unsigned long *rq_bmap, *sq_bmap, *cq_bmap;
+ struct qmem *rq_ctx, *sq_ctx, *cq_ctx;
+ struct qmem *rss_ctx, *nix_qints_ctx, *cq_ints_ctx;
+ unsigned long flags;
+
+ if (!aq) {
+ WARN_ON_ONCE(1);
+ rq_bmap = pfvf->rq_bmap;
+ sq_bmap = pfvf->sq_bmap;
+ cq_bmap = pfvf->cq_bmap;
+ rq_ctx = pfvf->rq_ctx;
+ sq_ctx = pfvf->sq_ctx;
+ cq_ctx = pfvf->cq_ctx;
+ rss_ctx = pfvf->rss_ctx;
+ nix_qints_ctx = pfvf->nix_qints_ctx;
+ cq_ints_ctx = pfvf->cq_ints_ctx;
+
+ pfvf->rq_bmap = NULL;
+ pfvf->sq_bmap = NULL;
+ pfvf->cq_bmap = NULL;
+ pfvf->rq_ctx = NULL;
+ pfvf->sq_ctx = NULL;
+ pfvf->cq_ctx = NULL;
+ pfvf->rss_ctx = NULL;
+ pfvf->nix_qints_ctx = NULL;
+ pfvf->cq_ints_ctx = NULL;
+ goto free_ctx;
+ }
+
+ spin_lock_irqsave(&aq->lock, flags);
+ rq_bmap = pfvf->rq_bmap;
+ sq_bmap = pfvf->sq_bmap;
+ cq_bmap = pfvf->cq_bmap;
+ rq_ctx = pfvf->rq_ctx;
+ sq_ctx = pfvf->sq_ctx;
+ cq_ctx = pfvf->cq_ctx;
+ rss_ctx = pfvf->rss_ctx;
+ nix_qints_ctx = pfvf->nix_qints_ctx;
+ cq_ints_ctx = pfvf->cq_ints_ctx;
pfvf->rq_bmap = NULL;
- pfvf->cq_bmap = NULL;
pfvf->sq_bmap = NULL;
+ pfvf->cq_bmap = NULL;
pfvf->rq_ctx = NULL;
pfvf->sq_ctx = NULL;
pfvf->cq_ctx = NULL;
pfvf->rss_ctx = NULL;
pfvf->nix_qints_ctx = NULL;
pfvf->cq_ints_ctx = NULL;
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+free_ctx:
+ kfree(rq_bmap);
+ kfree(sq_bmap);
+ kfree(cq_bmap);
+ if (rq_ctx)
+ qmem_free(rvu->dev, rq_ctx);
+ if (sq_ctx)
+ qmem_free(rvu->dev, sq_ctx);
+ if (cq_ctx)
+ qmem_free(rvu->dev, cq_ctx);
+ if (rss_ctx)
+ qmem_free(rvu->dev, rss_ctx);
+ if (nix_qints_ctx)
+ qmem_free(rvu->dev, nix_qints_ctx);
+ if (cq_ints_ctx)
+ qmem_free(rvu->dev, cq_ints_ctx);
}
static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
@@ -946,6 +1044,7 @@ static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
int rss_sz, int rss_grps, int hwctx_size,
u64 way_mask, bool tag_lsb_as_adder)
{
+ struct qmem *rss_ctx;
int err, grp, num_indices;
u64 val;
@@ -955,12 +1054,14 @@ static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
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)
+ err = qmem_alloc(rvu->dev, &rss_ctx, num_indices, hwctx_size);
+ if (err) {
+ qmem_free(rvu->dev, rss_ctx);
return err;
+ }
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_BASE(nixlf),
- (u64)pfvf->rss_ctx->iova);
+ (u64)rss_ctx->iova);
/* Config full RSS table size, enable RSS and caching */
val = BIT_ULL(36) | BIT_ULL(4) | way_mask << 20 |
@@ -974,6 +1075,13 @@ static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
for (grp = 0; grp < rss_grps; grp++)
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_GRPX(nixlf, grp),
((ilog2(rss_sz) - 1) << 16) | (rss_sz * grp));
+
+ err = nix_ctx_assign(rvu, blkaddr, &pfvf->rss_ctx, rss_ctx);
+ if (err) {
+ qmem_free(rvu->dev, rss_ctx);
+ return err;
+ }
+
return 0;
}
@@ -1504,7 +1612,10 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
struct nix_lf_alloc_req *req,
struct nix_lf_alloc_rsp *rsp)
{
+ struct qmem *cq_ints_ctx = NULL, *nix_qints_ctx = NULL;
int nixlf, qints, hwctx_size, intf, rc = 0, pf;
+ unsigned long *rq_bmap, *sq_bmap, *cq_bmap;
+ struct qmem *rq_ctx, *sq_ctx, *cq_ctx;
u16 bcast, mcast, promisc, ucast;
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = req->hdr.pcifunc;
@@ -1575,59 +1686,92 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
/* Alloc NIX RQ HW context memory and config the base */
hwctx_size = 1UL << ((ctx_cfg >> 4) & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &rq_ctx, req->rq_cnt, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, rq_ctx);
goto free_mem;
+ }
- pfvf->rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL);
- if (!pfvf->rq_bmap) {
+ rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL);
+ if (!rq_bmap) {
+ qmem_free(rvu->dev, rq_ctx);
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_BASE(nixlf),
- (u64)pfvf->rq_ctx->iova);
+ (u64)rq_ctx->iova);
/* Set caching and queue count in HW */
cfg = BIT_ULL(36) | (req->rq_cnt - 1) | req->way_mask << 20;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_CFG(nixlf), cfg);
+ rc = nix_qctx_assign(rvu, blkaddr, &pfvf->rq_ctx, &pfvf->rq_bmap,
+ rq_ctx, rq_bmap);
+ if (rc) {
+ kfree(rq_bmap);
+ qmem_free(rvu->dev, rq_ctx);
+ goto free_mem;
+ }
+
/* Alloc NIX SQ HW context memory and config the base */
hwctx_size = 1UL << (ctx_cfg & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &sq_ctx, req->sq_cnt, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, sq_ctx);
goto free_mem;
+ }
- pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
- if (!pfvf->sq_bmap) {
+ sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
+ if (!sq_bmap) {
+ qmem_free(rvu->dev, sq_ctx);
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_BASE(nixlf),
- (u64)pfvf->sq_ctx->iova);
+ (u64)sq_ctx->iova);
cfg = BIT_ULL(36) | (req->sq_cnt - 1) | req->way_mask << 20;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_CFG(nixlf), cfg);
+ rc = nix_qctx_assign(rvu, blkaddr, &pfvf->sq_ctx, &pfvf->sq_bmap,
+ sq_ctx, sq_bmap);
+ if (rc) {
+ kfree(sq_bmap);
+ qmem_free(rvu->dev, sq_ctx);
+ goto free_mem;
+ }
+
/* Alloc NIX CQ HW context memory and config the base */
hwctx_size = 1UL << ((ctx_cfg >> 8) & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &cq_ctx, req->cq_cnt, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, cq_ctx);
goto free_mem;
+ }
- pfvf->cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL);
- if (!pfvf->cq_bmap) {
+ cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL);
+ if (!cq_bmap) {
+ qmem_free(rvu->dev, cq_ctx);
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_BASE(nixlf),
- (u64)pfvf->cq_ctx->iova);
+ (u64)cq_ctx->iova);
cfg = BIT_ULL(36) | (req->cq_cnt - 1) | req->way_mask << 20;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_CFG(nixlf), cfg);
+ rc = nix_qctx_assign(rvu, blkaddr, &pfvf->cq_ctx, &pfvf->cq_bmap,
+ cq_ctx, cq_bmap);
+ if (rc) {
+ kfree(cq_bmap);
+ qmem_free(rvu->dev, cq_ctx);
+ goto free_mem;
+ }
+
/* Initialize receive side scaling (RSS) */
hwctx_size = 1UL << ((ctx_cfg >> 12) & 0xF);
rc = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz,
@@ -1640,29 +1784,45 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
qints = (cfg >> 24) & 0xFFF;
hwctx_size = 1UL << ((ctx_cfg >> 24) & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &cq_ints_ctx, qints, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, cq_ints_ctx);
goto free_mem;
+ }
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_BASE(nixlf),
- (u64)pfvf->cq_ints_ctx->iova);
+ (u64)cq_ints_ctx->iova);
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_CFG(nixlf),
BIT_ULL(36) | req->way_mask << 20);
+ rc = nix_ctx_assign(rvu, blkaddr, &pfvf->cq_ints_ctx, cq_ints_ctx);
+ if (rc) {
+ qmem_free(rvu->dev, cq_ints_ctx);
+ goto free_mem;
+ }
+
/* Alloc memory for QINT's HW contexts */
cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
qints = (cfg >> 12) & 0xFFF;
hwctx_size = 1UL << ((ctx_cfg >> 20) & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &nix_qints_ctx, qints, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, nix_qints_ctx);
goto free_mem;
+ }
rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_BASE(nixlf),
- (u64)pfvf->nix_qints_ctx->iova);
+ (u64)nix_qints_ctx->iova);
rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_CFG(nixlf),
BIT_ULL(36) | req->way_mask << 20);
+ rc = nix_ctx_assign(rvu, blkaddr, &pfvf->nix_qints_ctx, nix_qints_ctx);
+ if (rc) {
+ qmem_free(rvu->dev, nix_qints_ctx);
+ goto free_mem;
+ }
+
/* Setup VLANX TPID's.
* Use VLAN1 for 802.1Q
* and VLAN0 for 802.1AD.
@@ -1702,8 +1862,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
}
if (is_rep_dev(rvu, pcifunc)) {
- pfvf->tx_chan_base = RVU_SWITCH_LBK_CHAN;
- pfvf->tx_chan_cnt = 1;
+ nix_interface_set_chan(rvu, pfvf, 0, 0,
+ RVU_SWITCH_LBK_CHAN, 1);
goto exit;
}
@@ -1807,6 +1967,8 @@ int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct nix_lf_free_req *req,
nix_interface_deinit(rvu, pcifunc, nixlf);
free_lf:
+ nix_interface_clear_chan(rvu, pfvf);
+
/* Reset this NIX LF */
err = rvu_lf_reset(rvu, block, nixlf);
if (err) {
@@ -2100,15 +2262,22 @@ static void nix_clear_tx_xoff(struct rvu *rvu, int blkaddr,
rvu_write64(rvu, blkaddr, reg, 0x0);
}
-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
{
- struct rvu_hwinfo *hw = rvu->hw;
int pf = rvu_get_pf(rvu->pdev, pcifunc);
+ struct rvu_hwinfo *hw = rvu->hw;
+ struct rvu_pfvf *pfvf;
u8 cgx_id = 0, lmac_id = 0;
- if (is_lbk_vf(rvu, pcifunc)) {/* LBK links */
+ if (is_rep_dev(rvu, pcifunc))
return hw->cgx_links;
- } else if (is_pf_cgxmapped(rvu, pf)) {
+
+ if (is_lbk_vf(rvu, pcifunc)) {
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+ return hw->cgx_links + pfvf->lbkid;
+ }
+
+ if (is_pf_cgxmapped(rvu, pf)) {
rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
return (cgx_id * hw->lmac_per_cgx) + lmac_id;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index 42a601976db3..c1d0c36b47b5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -3545,6 +3545,53 @@ int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
return rc;
}
+int rvu_mbox_handler_npc_flow_del_n_free(struct rvu *rvu,
+ struct npc_flow_del_n_free_req *mreq,
+ struct msg_rsp *rsp)
+{
+ struct npc_mcam_free_entry_req sreq = { 0 };
+ struct npc_delete_flow_req dreq = { 0 };
+ struct npc_delete_flow_rsp drsp = { 0 };
+ u16 entry[256];
+ int ret = 0, i;
+ bool err = false;
+ u16 cnt;
+
+ sreq.hdr.pcifunc = mreq->hdr.pcifunc;
+ dreq.hdr.pcifunc = mreq->hdr.pcifunc;
+
+ cnt = mreq->cnt;
+ if (!cnt || cnt > 256) {
+ dev_err_ratelimited(rvu->dev, "Invalid cnt=%u\n", cnt);
+ return -EINVAL;
+ }
+
+ /* Snapshot shared mailbox memory before processing the request. */
+ memcpy(entry, mreq->entry, cnt * sizeof(entry[0]));
+
+ for (i = 0; i < cnt; i++) {
+ dreq.entry = entry[i];
+ ret = rvu_mbox_handler_npc_delete_flow(rvu, &dreq, &drsp);
+ if (ret) {
+ dev_err_ratelimited(rvu->dev,
+ "delete flow error for i=%d entry=%d\n",
+ i, entry[i]);
+ err = true;
+ }
+
+ sreq.entry = entry[i];
+ ret = rvu_mbox_handler_npc_mcam_free_entry(rvu, &sreq, rsp);
+ if (ret) {
+ dev_err_ratelimited(rvu->dev,
+ "free entry error for i=%d entry=%d\n",
+ i, entry[i]);
+ err = true;
+ }
+ }
+
+ return err ? -EINVAL : 0;
+}
+
int rvu_mbox_handler_npc_mcam_read_entry(struct rvu *rvu,
struct npc_mcam_read_entry_req *req,
struct npc_mcam_read_entry_rsp *rsp)
@@ -4444,6 +4491,83 @@ int rvu_mbox_handler_npc_mcam_entry_stats(struct rvu *rvu,
return 0;
}
+int rvu_mbox_handler_npc_mcam_mul_stats(struct rvu *rvu,
+ struct npc_mcam_get_mul_stats_req *req,
+ struct npc_mcam_get_mul_stats_rsp *rsp)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+ u16 req_cnt, index, cntr, mcam_entry;
+ u16 pcifunc = req->hdr.pcifunc;
+ int blkaddr, cnt = 0, i;
+ u16 entry[256];
+ u64 regval;
+ u32 bank;
+
+ rsp->cnt = 0;
+ memset(rsp->rsvd, 0, sizeof(rsp->rsvd));
+ memset(rsp->stat, 0, sizeof(rsp->stat));
+
+ req_cnt = req->cnt;
+ if (!req_cnt || req_cnt > 256) {
+ dev_err_ratelimited(rvu->dev, "%s invalid request cnt=%u\n",
+ __func__, req_cnt);
+ return -EINVAL;
+ }
+
+ /* Snapshot shared mailbox memory before processing the request. */
+ memcpy(entry, req->entry, req_cnt * sizeof(entry[0]));
+
+ blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+ if (blkaddr < 0)
+ return NPC_MCAM_INVALID_REQ;
+
+ mutex_lock(&mcam->lock);
+
+ for (i = 0; i < req_cnt; i++) {
+ mcam_entry = npc_cn20k_vidx2idx(entry[i]);
+
+ if (npc_mcam_verify_entry(mcam, pcifunc, mcam_entry)) {
+ mutex_unlock(&mcam->lock);
+ dev_err_ratelimited(rvu->dev, "%s invalid mcam index=%d\n",
+ __func__, entry[i]);
+ memset(rsp->stat, 0, sizeof(rsp->stat));
+ rsp->cnt = 0;
+ return -EINVAL;
+ }
+
+ index = mcam_entry & (mcam->banksize - 1);
+ bank = npc_get_bank(mcam, mcam_entry);
+
+ if (is_cn20k(rvu->pdev)) {
+ regval = rvu_read64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index,
+ bank));
+ rsp->stat[cnt] = regval;
+ cnt++;
+ continue;
+ }
+
+ /* read MCAM entry STAT_ACT register */
+ regval = rvu_read64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank));
+
+ if (!(regval & rvu->hw->npc_stat_ena)) {
+ rsp->stat[cnt] = 0;
+ cnt++;
+ continue;
+ }
+
+ cntr = regval & 0x1FF;
+
+ rsp->stat[cnt] = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(cntr));
+ rsp->stat[cnt] &= BIT_ULL(48) - 1;
+ cnt++;
+ }
+
+ rsp->cnt = cnt;
+ mutex_unlock(&mcam->lock);
+ return 0;
+}
+
void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf)
{
struct npc_mcam *mcam = &rvu->hw->mcam;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
index ec004ea5154c..bc66d5c9a659 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
@@ -1930,6 +1930,20 @@ static int npc_delete_flow(struct rvu *rvu, struct rvu_npc_mcam_rule *rule,
return rvu_mbox_handler_npc_mcam_dis_entry(rvu, &dis_req, &dis_rsp);
}
+int rvu_mbox_handler_npc_mcam_get_features(struct rvu *rvu,
+ struct msg_req *req,
+ struct npc_mcam_get_features_rsp *rsp)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+
+ mutex_lock(&mcam->lock);
+ rsp->rx_features = mcam->rx_features;
+ rsp->tx_features = mcam->tx_features;
+ mutex_unlock(&mcam->lock);
+
+ return 0;
+}
+
int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,
struct npc_delete_flow_req *req,
struct npc_delete_flow_rsp *rsp)
@@ -1938,7 +1952,7 @@ int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,
struct rvu_npc_mcam_rule *iter, *tmp;
u16 pcifunc = req->hdr.pcifunc;
struct list_head del_list;
- int blkaddr;
+ int blkaddr, err;
req->entry = npc_cn20k_vidx2idx(req->entry);
req->start = npc_cn20k_vidx2idx(req->start);
@@ -1969,17 +1983,20 @@ int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,
}
mutex_unlock(&mcam->lock);
+ err = 0;
list_for_each_entry_safe(iter, tmp, &del_list, list) {
u16 entry = iter->entry;
/* clear the mcam entry target pcifunc */
mcam->entry2target_pffunc[entry] = 0x0;
- if (npc_delete_flow(rvu, iter, pcifunc))
+ if (npc_delete_flow(rvu, iter, pcifunc)) {
dev_err(rvu->dev, "rule deletion failed for entry:%u",
entry);
+ err = -EINVAL;
+ }
}
- return 0;
+ return err;
}
static int npc_update_dmac_value(struct rvu *rvu, int npcblkaddr,
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2 net-next] octeontx2-af: Add couple of mailbox support.
2026-09-24 2:44 [PATCH v2 net-next] octeontx2-af: Add couple of mailbox support Ratheesh Kannoth
@ 2026-09-28 3:01 ` netdev-bot+sashiko
0 siblings, 0 replies; 4+ messages in thread
From: netdev-bot+sashiko @ 2026-09-28 3:01 UTC (permalink / raw)
To: rkannoth
Cc: linux-kernel, netdev, andrew+netdev, davem, edumazet, kuba,
pabeni, sgoutham
Thank you for your contribution! Sashiko AI review found 8 potential
issue(s) to consider.
Critical: 0 (1) · High: 1 (4) · Medium: 5 (1) · Low: 2
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [High] rvu_mbox_handler_nix_lf_alloc() now declares the locals `struct
qmem *rq_ctx, *sq_ctx, *cq_ctx;` without initializing them, and…
- [Medium] The patch rewrites nix_get_tx_link() (rvu_nix.c) and changes
what it returns for existing users, not only for the new…
- [Medium] IFACE_GET_INFO treats `pfvf->tx_chan_cnt != 0` as meaning the
interface has a live NIX LF, but the patch clears the channel fields…
- [Medium] In nix_interface_init(), `lbkid = 0; if (rvu->hw->lbk_links >
1) lbkid = ...; if (loop) lbkid = !lbkid;` sets lbkid=1 even when…
- [Medium] The new rvu_mbox_handler_npc_mcam_mul_stats() decodes the
counter selector as `cntr = regval & 0x1FF` before reading…
- [Medium] rvu_mbox_handler_nix_lf_free() now calls
nix_interface_clear_chan(), which sets rx_chan_base to 0.
- [Low] For the representor, nix_get_tx_link() now returns hw->cgx_links,
and IFACE_GET_INFO reports that value.
- [Low] nix_interface_init() writes pfvf->lbkid without holding
rsrc_lock, before nix_interface_set_chan().
Pre-existing issues:
- [Critical] This problem wasn't introduced by this patch, but
__rvu_mbox_handler() sets msg->pcifunc in the peer-writable shared…
- [High] This problem wasn't introduced by this patch, but
rvu_mbox_handler_nix_lf_alloc() never checks whether…
- [High] This problem wasn't introduced by this patch, but
rvu_nix_blk_aq_enq_inst() checks `pfvf->rq_ctx && req->qidx <…
- [High] This problem wasn't introduced by this patch, but
rvu_nix_blk_aq_enq_inst() holds spin_lock(&aq->lock) across…
- [High] This problem wasn't introduced by this patch, but
__rvu_mbox_handler() computes the next message address as `offset =…
- [Medium] This problem wasn't introduced by this patch, but the existing
rvu_mbox_handler_npc_mcam_entry_stats() never calls…
--
octeontx2-af: Add couple of mailbox support.
This patch adds an IFACE_GET_INFO mailbox handler for the switch PF. It
also adds NPC handlers for batched flow delete/free, multi-entry stats and
feature queries. NIX channel fields are now published under rsrc_lock and
NIX context pointers under the AQ lock, and nix_get_tx_link() is exported
with new LBK and representor handling.
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
> index 30e148291581c..055e39746558a 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
[ ... ]
> +int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req,
> + struct iface_get_info_rsp *rsp)
> +{
> + struct iface_info *info;
> + bool truncated = false;
> + struct rvu_pfvf *pfvf;
> + int pf, vf, numvfs;
> + int tot = 0;
> + u16 pcifunc;
> + u64 cfg;
> +
> + if (!rvu_is_switch_pcifunc(rvu, req->hdr.pcifunc))
> + return -EPERM;
[Severity: Critical]
This is a pre-existing issue, but this authorization check trusts
req->hdr.pcifunc, and that field is in the shared mailbox, which the peer
can write.
__rvu_mbox_handler() rewrites the header in place and then hands the same
message to the handler:
msg->pcifunc &= rvu_pcifunc_pf_mask(rvu->pdev);
msg->pcifunc |= rvu_make_pcifunc(rvu->pdev, devid, 0);
Suppose a PF/VF keeps rewriting pcifunc in its mailbox region after that
read-modify-write. Could the handler then see a forged identity here and
pass the switch-only check?
The new rvu_mbox_handler_npc_flow_del_n_free() has the same problem. It
copies mreq->hdr.pcifunc into its sub-requests. With a forged pcifunc of
0, npc_mcam_verify_entry() returns early:
if (is_pffunc_af(pcifunc))
return 0;
That skips both the bounds check and the ownership check. Could an
untrusted function use this to delete or free MCAM entries that belong to
other functions?
[ ... ]
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
> index 93c5c4b298bf2..26836c243c500 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
[ ... ]
> @@ -337,6 +336,28 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
> return true;
> }
>
[ ... ]
> +static void nix_interface_clear_chan(struct rvu *rvu, struct rvu_pfvf *pfvf)
> +{
> + mutex_lock(&rvu->rsrc_lock);
> + pfvf->rx_chan_base = 0;
> + pfvf->rx_chan_cnt = 0;
> + pfvf->tx_chan_base = 0;
> + pfvf->tx_chan_cnt = 0;
> + mutex_unlock(&rvu->rsrc_lock);
> +}
[Severity: Medium]
Is 0 a safe cleared value for rx_chan_base? Channel 0 is a real hardware
channel. Also, pfvf->nixlf stays attached after NIX_LF_FREE, and the otx2
driver sends NIX_LF_FREE when the interface goes down.
Existing handlers still use rx_chan_base after that. The representor path
in rvu_mbox_handler_npc_install_flow() does:
if (from_rep_dev)
req->channel = pfvf->rx_chan_base;
So a flow installed for a representee that is down would match channel 0.
That rule is not rewritten when the LF is allocated again.
nix_bp_enable() and nix_bp_disable() both compute:
chan_base = pfvf->rx_chan_base + req->chan_base;
and then read-modify-write NIX_AF_RX_CHANX_CFG. With rx_chan_base cleared,
could they program the BPID and the BP enable bit on another interface's
channels? For LBK VFs, nix_bp_disable() would also free the BPID it read
from that other channel.
Before this patch, rx_chan_base kept the function's own channel after
LF_FREE.
[ ... ]
> @@ -410,14 +430,13 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
> * 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;
> + nix_interface_set_chan(rvu, pfvf,
> + rvu_nix_chan_lbk(rvu, lbkid, vf), 1,
> + vf & 0x1 ?
> + rvu_nix_chan_lbk(rvu, lbkid, vf - 1) :
> + rvu_nix_chan_lbk(rvu, lbkid, vf + 1), 1);
[Severity: Low]
pfvf->lbkid is still written outside rsrc_lock here.
rvu_mbox_handler_iface_get_info() reads it under rsrc_lock through
nix_get_tx_link().
AFVF mailbox work and the switch PF's AFPF work don't share a lock.
Suppose an LBK VF sends a second NIX_LF_ALLOC while its channels are
still published (nothing rejects this), and NIX_LF_LBK_BLK_SEL flips
lbkid. Can a concurrent IFACE_GET_INFO then pair the new tx_link with the
old channel values?
[ ... ]
> @@ -912,33 +929,114 @@ static void nix_setup_lso(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
> nix_hw->lso.in_use++;
> }
>
> +static int nix_qctx_assign(struct rvu *rvu, int blkaddr, struct qmem **ctx,
> + unsigned long **bmap, struct qmem *new_ctx,
> + unsigned long *new_bmap)
> +{
> + struct admin_queue *aq = rvu->hw->block[blkaddr].aq;
> + unsigned long flags;
> +
> + if (!aq) {
> + WARN_ON_ONCE(1);
> + return -ENODEV;
> + }
> +
> + spin_lock_irqsave(&aq->lock, flags);
> + *ctx = new_ctx;
> + *bmap = new_bmap;
> + spin_unlock_irqrestore(&aq->lock, flags);
[Severity: High]
This isn't a bug introduced by this patch, but what happens to the old
pfvf->rq_ctx and rq_bmap if a function sends NIX_LF_ALLOC twice without a
NIX_LF_FREE in between? The same question applies to the SQ, CQ, RSS,
CINT and QINT contexts.
rvu_mbox_handler_nix_lf_alloc() only checks:
if (!pfvf->nixlf || blkaddr < 0)
return NIX_AF_ERR_AF_LF_INVALID;
These helpers then overwrite the pointers, as the old
qmem_alloc(&pfvf->rq_ctx, ...) calls did. Does each repeated
NIX_LF_ALLOC leak the DMA coherent context buffers and the kcalloc'd
bitmaps? The devm qmem structs are only reclaimed at unbind.
[ ... ]
> static void nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
> {
[ ... ]
> + spin_lock_irqsave(&aq->lock, flags);
> + rq_bmap = pfvf->rq_bmap;
> + sq_bmap = pfvf->sq_bmap;
> + cq_bmap = pfvf->cq_bmap;
[ ... ]
> pfvf->rss_ctx = NULL;
> pfvf->nix_qints_ctx = NULL;
> pfvf->cq_ints_ctx = NULL;
> + spin_unlock_irqrestore(&aq->lock, flags);
> +
> +free_ctx:
> + kfree(rq_bmap);
> + kfree(sq_bmap);
> + kfree(cq_bmap);
[Severity: High]
This is a pre-existing issue, but the new aq->lock handling only protects
the IFACE_GET_INFO reader. rvu_nix_blk_aq_enq_inst() validates the context
before it takes aq->lock:
if (!pfvf->rq_ctx || req->qidx >= pfvf->rq_ctx->qsize)
rc = NIX_AF_ERR_AQ_ENQUEUE;
After nix_aq_enqueue_wait(), it touches the bitmap under aq->lock without
checking again:
if (req->ctype == NIX_AQ_CTYPE_RQ && req->rq.ena)
__set_bit(req->qidx, pfvf->rq_bmap);
__rvu_flr_handler() takes only flr_lock, and the AFVF mailbox work takes
no lock. So an FLR teardown can run nix_ctx_free() at the same time. Can
the AQ path then call __set_bit() on a NULL bitmap, or read
rq_ctx->qsize after qmem_free()?
[Severity: High]
This isn't a bug introduced by this patch either, but can
rvu_nix_blk_aq_enq_inst() sleep while holding aq->lock? It holds
spin_lock(&aq->lock) across nix_aq_enqueue_wait(). When the completion
code is CTX_FAULT, LOCKERR or CTX_POISON, that leads to:
nix_aq_enqueue_wait()
rvu_ndc_fix_locked_cacheline()
rvu_poll_reg()
usleep_range(1, 5)
On non-CN20K parts, rvu_poll_reg() sleeps whenever the NDC busy bits are
not already clear.
[ ... ]
> @@ -1504,7 +1612,10 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
> struct nix_lf_alloc_req *req,
> struct nix_lf_alloc_rsp *rsp)
> {
> + struct qmem *cq_ints_ctx = NULL, *nix_qints_ctx = NULL;
> int nixlf, qints, hwctx_size, intf, rc = 0, pf;
> + unsigned long *rq_bmap, *sq_bmap, *cq_bmap;
> + struct qmem *rq_ctx, *sq_ctx, *cq_ctx;
[ ... ]
> @@ -1575,59 +1686,92 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
>
> /* Alloc NIX RQ HW context memory and config the base */
> hwctx_size = 1UL << ((ctx_cfg >> 4) & 0xF);
> - rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
> - if (rc)
> + rc = qmem_alloc(rvu->dev, &rq_ctx, req->rq_cnt, hwctx_size);
> + if (rc) {
> + qmem_free(rvu->dev, rq_ctx);
> goto free_mem;
> + }
[Severity: High]
Can rq_ctx be uninitialized here? qmem_alloc() returns without writing *q
when qsize is 0:
if (!qsize)
return -EINVAL;
The handler checks rq_cnt, sq_cnt and cq_cnt for zero at the top.
However, req points into the mailbox, which the peer can write, and the
fields are read again here. That re-read happens after rvu_lf_reset(),
which can poll for around 20ms.
If a PF or AF VF sets the count to 0 in that window, qmem_free() gets an
uninitialized stack pointer. It then passes it to dma_free_attrs() and
devm_kfree().
The same applies to sq_ctx and cq_ctx below. It also applies to rss_ctx
in nixlf_rss_ctx_init() if rss_grps is changed to 0 after validation.
The v2 notes say cq_ints_ctx and nix_qints_ctx were NULL-initialized "so
the error path does not pass an uninitialized pointer to qmem_free()".
Should rq_ctx, sq_ctx, cq_ctx and rss_ctx be initialized the same way?
[ ... ]
> @@ -1702,8 +1862,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
> }
>
> if (is_rep_dev(rvu, pcifunc)) {
> - pfvf->tx_chan_base = RVU_SWITCH_LBK_CHAN;
> - pfvf->tx_chan_cnt = 1;
> + nix_interface_set_chan(rvu, pfvf, 0, 0,
> + RVU_SWITCH_LBK_CHAN, 1);
> goto exit;
> }
[Severity: Low]
This path jumps to exit before nix_interface_init(), so rsp->tx_link is
never set for the representor. With this patch, nix_get_tx_link() returns
hw->cgx_links for the representor, and IFACE_GET_INFO reports that value.
So NIX_LF_ALLOC and IFACE_GET_INFO report different links for the
representor.
This doesn't seem to misprogram hardware. otx2_txschq_config() skips the
NIX_AF_TL3_TL2X_LINKX_CFG write when is_otx2_sdp_rep() is true, and the
missing rsp->tx_link predates this patch. Should the two responses still
agree?
[ ... ]
> @@ -1807,6 +1967,8 @@ int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct nix_lf_free_req *req,
> nix_interface_deinit(rvu, pcifunc, nixlf);
>
> free_lf:
> + nix_interface_clear_chan(rvu, pfvf);
> +
[Severity: Medium]
IFACE_GET_INFO treats a non-zero tx_chan_cnt as meaning the function has a
live NIX LF:
if (!pfvf->tx_chan_cnt)
goto chk_vfs;
This is the only place the channels are cleared, though. Two other paths
free the NIX contexts and leave the channels set.
The first is rvu_nix_lf_teardown(), which runs on FLR and detach. It calls
nix_interface_deinit(), nix_txschq_free() and nix_ctx_free(), but not
nix_interface_clear_chan().
The second is rvu_mbox_handler_nix_lf_alloc(). It can fail after
nix_interface_init() has already called nix_interface_set_chan(), for
example when nix_update_mce_rule() fails. It then goes through
free_dft/free_mem to nix_ctx_free() without clearing the channels.
In both cases, would IFACE_GET_INFO keep listing the function with its
old channel base/count and tx_link, but zero queue counts?
[ ... ]
> @@ -2100,15 +2262,22 @@ static void nix_clear_tx_xoff(struct rvu *rvu, int blkaddr,
> rvu_write64(rvu, blkaddr, reg, 0x0);
> }
>
> -static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
> +int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
> {
> - struct rvu_hwinfo *hw = rvu->hw;
> int pf = rvu_get_pf(rvu->pdev, pcifunc);
> + struct rvu_hwinfo *hw = rvu->hw;
> + struct rvu_pfvf *pfvf;
> u8 cgx_id = 0, lmac_id = 0;
>
> - if (is_lbk_vf(rvu, pcifunc)) {/* LBK links */
> + if (is_rep_dev(rvu, pcifunc))
> return hw->cgx_links;
> - } else if (is_pf_cgxmapped(rvu, pf)) {
> +
> + if (is_lbk_vf(rvu, pcifunc)) {
> + pfvf = rvu_get_pfvf(rvu, pcifunc);
> + return hw->cgx_links + pfvf->lbkid;
> + }
[Severity: Medium]
This changes what the existing TX scheduler callers get back, not only
what IFACE_GET_INFO sees.
Before this patch, every LBK VF got hw->cgx_links. The representor is not
CGX mapped, so it got the SDP link, cgx_links + lbk_links. Now LBK VFs get
cgx_links + lbkid and the representor gets cgx_links.
The existing callers use this value as a TL1 index:
rvu_mbox_handler_nix_txsch_alloc() uses start = end = link at the
aggregation level.
nix_tl1_default_cfg() writes NIX_AF_TL1X_TOPOLOGY/SCHEDULE/CIR(link).
nix_txschq_free() clears TL1 SW_XOFF and, for PFs, CFG_DONE on
TL1[link].
is_valid_txschq() uses it to decide whether aggregation-level TLs are
shared.
With lbk_links > 1, LBK VFs with lbkid 1 move to TL1[cgx_links + 1]. The
representor PF moves onto LBK0's TL1, which the AF VFs share. Since the
representor is a PF, won't its nix_txschq_free() now clear CFG_DONE on
that shared TL1?
On fixed-mapping silicon, nix_get_txschq_range() computes the LBK start
as:
*start = hw->cap.nix_txsch_per_cgx_lmac * link;
That was only correct while link was always cgx_links. Can a non-zero
lbkid now produce overlapping ranges?
The commit message doesn't mention this change to existing TX behaviour.
If it is meant as a fix, could it go in a separate patch with a Fixes:
tag?
[Severity: Medium]
Is lbkid always below hw->lbk_links here? nix_interface_init() does:
lbkid = 0;
if (rvu->hw->lbk_links > 1)
lbkid = vf & 0x1 ? 0 : 1;
...
if (loop)
lbkid = !lbkid;
So on single-LBK silicon, an LBK VF that passes NIX_LF_LBK_BLK_SEL ends up
with lbkid 1. nix_get_tx_link() then returns cgx_links + 1, which is the
SDP link index (cgx_links + lbk_links).
Would that VF's TL1 allocation, its nix_tl1_default_cfg() writes, the
SW_XOFF clearing in nix_txschq_free() and the is_valid_txschq() sharing
checks all hit the SDP TL1? On fixed-mapping silicon, its schq range
would also collide with the SDP range.
[ ... ]
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
> index 42a601976db36..c1d0c36b47b55 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
> @@ -3545,6 +3545,53 @@ int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
> return rc;
> }
>
> +int rvu_mbox_handler_npc_flow_del_n_free(struct rvu *rvu,
> + struct npc_flow_del_n_free_req *mreq,
> + struct msg_rsp *rsp)
> +{
[ ... ]
> + cnt = mreq->cnt;
> + if (!cnt || cnt > 256) {
> + dev_err_ratelimited(rvu->dev, "Invalid cnt=%u\n", cnt);
> + return -EINVAL;
> + }
> +
> + /* Snapshot shared mailbox memory before processing the request. */
> + memcpy(entry, mreq->entry, cnt * sizeof(entry[0]));
[Severity: High]
This is a pre-existing issue, but the dispatcher never bounds-checks where
a request sits. __rvu_mbox_handler() computes the next message as:
offset = mbox->rx_start + msg->next_msgoff;
It doesn't check this offset, or the request size, against the MBOX_SIZE
mapping (ioremap_wc(bar4, MBOX_SIZE) for AF VFs). A peer could place this
request near the end of the mapping. Could this memcpy() of up to 512
bytes then read past the mapped mailbox and fault?
The same applies to the memcpy() in rvu_mbox_handler_npc_mcam_mul_stats().
[ ... ]
> @@ -4444,6 +4491,83 @@ int rvu_mbox_handler_npc_mcam_entry_stats(struct rvu *rvu,
> return 0;
> }
>
> +int rvu_mbox_handler_npc_mcam_mul_stats(struct rvu *rvu,
> + struct npc_mcam_get_mul_stats_req *req,
> + struct npc_mcam_get_mul_stats_rsp *rsp)
> +{
[ ... ]
> + for (i = 0; i < req_cnt; i++) {
> + mcam_entry = npc_cn20k_vidx2idx(entry[i]);
> +
> + if (npc_mcam_verify_entry(mcam, pcifunc, mcam_entry)) {
[Severity: Medium]
This isn't a bug introduced by this patch. The new batch handler checks
ownership here, but the existing single-entry
rvu_mbox_handler_npc_mcam_entry_stats() does not:
index = req->entry & (mcam->banksize - 1);
bank = npc_get_bank(mcam, req->entry);
npc_get_bank() has no upper bound on CN20K or for non-X2 key sizes. Can a
PF/VF use NPC_MCAM_ENTRY_STATS to read the hit counters of MCAM rules
owned by other functions, or to build out-of-range register offsets?
[ ... ]
> + /* read MCAM entry STAT_ACT register */
> + regval = rvu_read64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank));
> +
> + if (!(regval & rvu->hw->npc_stat_ena)) {
> + rsp->stat[cnt] = 0;
> + cnt++;
> + continue;
> + }
> +
> + cntr = regval & 0x1FF;
> +
> + rsp->stat[cnt] = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(cntr));
[Severity: Medium]
Does the 0x1FF mask drop the extended counter selector bits?
npc_map_mcam_entry_and_cntr() programs STAT_ACT as:
((cntr >> 9) << 12) | hw->npc_stat_ena | cntr
The comment in rvu_npc.c describes STAT_SEL_EXT at [14:12] on 98xx and a
12-bit STAT_SEL on CN10K. For an entry mapped to counter N where N is 512
or higher, wouldn't this return counter (N mod 512) instead?
The existing single-entry handler truncates the same way, but this patch
adds a second copy of that code.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260924024446.1851115-1-rkannoth%40marvell.com
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2 net-next] octeontx2-af: Add couple of mailbox support.
2026-09-23 3:28 Ratheesh Kannoth
@ 2026-09-23 3:59 ` Ratheesh Kannoth
0 siblings, 0 replies; 4+ messages in thread
From: Ratheesh Kannoth @ 2026-09-23 3:59 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham
On 2026-09-23 at 08:58:16, Ratheesh Kannoth (rkannoth@marvell.com) wrote:
> Add IFACE_GET_INFO and NPC batch flow/stats/features handlers for the
> RVU switch PF, with synchronized NIX context and channel publishing.
>
> IFACE_GET_INFO: report PF/VF channel and queue topology
> NPC_MCAM_FLOW_DEL_N_FREE: delete flows and free MCAM entries in bulk
> NPC_MCAM_GET_MUL_STATS: read MCAM counter stats in bulk
> NPC_MCAM_GET_FEATURES: report supported MCAM features
>
> Rework NIX LF context memory publish and teardown so queue context
> pointers and bitmaps are assigned and cleared under the NIX admin queue
> lock. Publish interface channel fields under rsrc_lock so IFACE_GET_INFO
> can read a consistent snapshot.
>
> Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
>
> ---
>
> v1 -> v2:
> - Drop SWDEV2AF_NOTIFY and the switch skeleton hooks from this patch;
> keep the series focused on the AF mailbox handlers above.
> - Restrict IFACE_GET_INFO to the PAN RVU switch PF (rvu_is_switch_pcifunc())
> and return -EPERM for other callers.
> - Hold rsrc_lock while building the IFACE_GET_INFO topology snapshot.
> - Publish and clear NIX interface channel fields under rsrc_lock.
> - Publish NIX LF queue context pointers and bitmaps under the admin queue
> lock; propagate assign failures and free partial allocations on error.
> - Fix nix_get_tx_link() for representor and LBK VF devices.
> - Propagate NPC flow deletion failures from npc_delete_flow() and the
> bulk delete/free handler; ratelimit bulk handler error logs.
> - Read NPC MCAM features under mcam->lock and clear mul-stats response
> on entry verification failure.
> https://lore.kernel.org/netdev/20260918050021.1359606-1-rkannoth@marvell.com/
Please ignore this version as it was sent with an incorrect commit message and changelog.
Apologies for the noise.
pw-bot: changes-requested
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 net-next] octeontx2-af: Add couple of mailbox support.
@ 2026-09-23 3:28 Ratheesh Kannoth
2026-09-23 3:59 ` Ratheesh Kannoth
0 siblings, 1 reply; 4+ messages in thread
From: Ratheesh Kannoth @ 2026-09-23 3:28 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham, Ratheesh Kannoth
Add IFACE_GET_INFO and NPC batch flow/stats/features handlers for the
RVU switch PF, with synchronized NIX context and channel publishing.
IFACE_GET_INFO: report PF/VF channel and queue topology
NPC_MCAM_FLOW_DEL_N_FREE: delete flows and free MCAM entries in bulk
NPC_MCAM_GET_MUL_STATS: read MCAM counter stats in bulk
NPC_MCAM_GET_FEATURES: report supported MCAM features
Rework NIX LF context memory publish and teardown so queue context
pointers and bitmaps are assigned and cleared under the NIX admin queue
lock. Publish interface channel fields under rsrc_lock so IFACE_GET_INFO
can read a consistent snapshot.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
v1 -> v2:
- Drop SWDEV2AF_NOTIFY and the switch skeleton hooks from this patch;
keep the series focused on the AF mailbox handlers above.
- Restrict IFACE_GET_INFO to the PAN RVU switch PF (rvu_is_switch_pcifunc())
and return -EPERM for other callers.
- Hold rsrc_lock while building the IFACE_GET_INFO topology snapshot.
- Publish and clear NIX interface channel fields under rsrc_lock.
- Publish NIX LF queue context pointers and bitmaps under the admin queue
lock; propagate assign failures and free partial allocations on error.
- Fix nix_get_tx_link() for representor and LBK VF devices.
- Propagate NPC flow deletion failures from npc_delete_flow() and the
bulk delete/free handler; ratelimit bulk handler error logs.
- Read NPC MCAM features under mcam->lock and clear mul-stats response
on entry verification failure.
https://lore.kernel.org/netdev/20260918050021.1359606-1-rkannoth@marvell.com/
---
.../net/ethernet/marvell/octeontx2/af/mbox.h | 62 ++++
.../net/ethernet/marvell/octeontx2/af/rvu.c | 159 ++++++++++
.../net/ethernet/marvell/octeontx2/af/rvu.h | 2 +
.../ethernet/marvell/octeontx2/af/rvu_nix.c | 291 ++++++++++++++----
.../ethernet/marvell/octeontx2/af/rvu_npc.c | 124 ++++++++
.../marvell/octeontx2/af/rvu_npc_fs.c | 23 +-
6 files changed, 597 insertions(+), 64 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index cece197d1074..28f2b549780f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -164,6 +164,8 @@ M(PTP_GET_CAP, 0x00c, ptp_get_cap, msg_req, ptp_get_cap_rsp) \
M(GET_REP_CNT, 0x00d, get_rep_cnt, msg_req, get_rep_cnt_rsp) \
M(ESW_CFG, 0x00e, esw_cfg, esw_cfg_req, msg_rsp) \
M(REP_EVENT_NOTIFY, 0x00f, rep_event_notify, rep_event, msg_rsp) \
+M(IFACE_GET_INFO, 0x014, iface_get_info, msg_req, \
+ iface_get_info_rsp) \
/* CGX mbox IDs (range 0x200 - 0x3FF) */ \
M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \
M(CGX_STOP_RXTX, 0x201, cgx_stop_rxtx, msg_req, msg_rsp) \
@@ -309,8 +311,16 @@ M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601e, npc_get_dft_rl_idxs, \
M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601f, npc_get_pfl_info, \
msg_req, \
npc_get_pfl_info_rsp) \
+M(NPC_MCAM_FLOW_DEL_N_FREE, 0x6020, npc_flow_del_n_free, \
+ npc_flow_del_n_free_req, msg_rsp) \
M(NPC_MCAM_READ_DEFAULT_RULE, 0x6021, npc_read_default_rule, msg_req, \
npc_mcam_read_base_rule_rsp) \
+M(NPC_MCAM_GET_MUL_STATS, 0x6022, npc_mcam_mul_stats, \
+ npc_mcam_get_mul_stats_req, \
+ npc_mcam_get_mul_stats_rsp) \
+M(NPC_MCAM_GET_FEATURES, 0x6023, npc_mcam_get_features, \
+ msg_req, \
+ npc_mcam_get_features_rsp) \
/* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \
M(NIX_LF_ALLOC, 0x8000, nix_lf_alloc, \
nix_lf_alloc_req, nix_lf_alloc_rsp) \
@@ -1586,6 +1596,31 @@ struct npc_mcam_alloc_entry_rsp {
u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];
};
+struct npc_flow_del_n_free_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 entry[256]; /* Entry index to be freed */
+};
+
+struct npc_mcam_get_features_rsp {
+ struct mbox_msghdr hdr;
+ u64 rx_features;
+ u64 tx_features;
+};
+
+struct npc_mcam_get_mul_stats_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 entry[256]; /* mcam entry */
+};
+
+struct npc_mcam_get_mul_stats_rsp {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 rsvd[3]; /* explicit padding for stat[] 8-byte alignment */
+ u64 stat[256]; /* counter stats */
+};
+
struct npc_mcam_free_entry_req {
struct mbox_msghdr hdr;
u16 entry; /* Entry index to be freed */
@@ -1812,6 +1847,33 @@ struct rep_event {
struct rep_evt_data evt_data;
};
+struct iface_info {
+ u8 is_vf : 1;
+ u8 is_sdp : 1;
+ u8 rsvd : 6;
+ u16 pcifunc;
+ u16 rx_chan_base;
+ u16 tx_chan_base;
+ u16 sq_cnt;
+ u16 cq_cnt;
+ u16 rq_cnt;
+ u8 rx_chan_cnt;
+ u8 tx_chan_cnt;
+ u8 tx_link;
+ u8 nix;
+};
+
+/* Max supported */
+#define IFACE_MAX (256 + 32) /* 32 PFs + 256 VFs */
+
+struct iface_get_info_rsp {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u8 truncated;
+ u8 rsvd[5];
+ struct iface_info info[IFACE_MAX];
+};
+
struct flow_msg {
unsigned char dmac[6];
unsigned char smac[6];
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index 30e148291581..055e39746558 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -1990,6 +1990,165 @@ int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,
return 0;
}
+static bool rvu_is_switch_pcifunc(struct rvu *rvu, u16 pcifunc)
+{
+ int pf, domain_nr;
+ struct pci_dev *pdev;
+ bool is_switch;
+
+ if (pcifunc & RVU_PFVF_FUNC_MASK)
+ return false;
+
+ pf = rvu_get_pf(rvu->pdev, pcifunc);
+ domain_nr = pci_domain_nr(rvu->pdev->bus);
+ pdev = pci_get_domain_bus_and_slot(domain_nr,
+ pf + rvu->pdev->bus->number, 0);
+ if (!pdev)
+ return false;
+
+ is_switch = pdev->device == PCI_DEVID_PAN_RVU;
+ pci_dev_put(pdev);
+
+ return is_switch;
+}
+
+static void rvu_iface_get_qcnts(struct rvu *rvu, struct rvu_pfvf *pfvf,
+ struct iface_info *info)
+{
+ struct admin_queue *aq;
+
+ info->sq_cnt = 0;
+ info->cq_cnt = 0;
+ info->rq_cnt = 0;
+
+ aq = rvu->hw->block[pfvf->nix_blkaddr].aq;
+ if (!aq)
+ return;
+
+ spin_lock(&aq->lock);
+
+ /* Use each LF queue context size; bitmaps are sized to qsize longs. */
+ if (pfvf->sq_ctx && pfvf->sq_bmap)
+ info->sq_cnt = bitmap_weight(pfvf->sq_bmap, pfvf->sq_ctx->qsize);
+ if (pfvf->cq_ctx && pfvf->cq_bmap)
+ info->cq_cnt = bitmap_weight(pfvf->cq_bmap, pfvf->cq_ctx->qsize);
+ if (pfvf->rq_ctx && pfvf->rq_bmap)
+ info->rq_cnt = bitmap_weight(pfvf->rq_bmap, pfvf->rq_ctx->qsize);
+
+ spin_unlock(&aq->lock);
+}
+
+int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req,
+ struct iface_get_info_rsp *rsp)
+{
+ struct iface_info *info;
+ bool truncated = false;
+ struct rvu_pfvf *pfvf;
+ int pf, vf, numvfs;
+ int tot = 0;
+ u16 pcifunc;
+ u64 cfg;
+
+ if (!rvu_is_switch_pcifunc(rvu, req->hdr.pcifunc))
+ return -EPERM;
+
+ /* Read-only topology snapshot for switch software. */
+ rsp->cnt = 0;
+ rsp->truncated = 0;
+ memset(rsp->rsvd, 0, sizeof(rsp->rsvd));
+ /* Preserve mbox_msghdr fields pre-filled by the mbox framework. */
+ memset(rsp->info, 0, sizeof(rsp->info));
+ info = rsp->info;
+
+ mutex_lock(&rvu->rsrc_lock);
+ for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+ if (tot >= IFACE_MAX) {
+ truncated = true;
+ goto done;
+ }
+
+ cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+ numvfs = (cfg >> 12) & 0xFF;
+
+ /* Skip not enabled PFs */
+ if (!(cfg & BIT_ULL(20)))
+ goto chk_vfs;
+
+ /* If Admin function, check on VFs */
+ if (cfg & BIT_ULL(21))
+ goto chk_vfs;
+
+ pcifunc = rvu_make_pcifunc(rvu->pdev, pf, 0);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+ /* Populate iff at least one Tx channel */
+ if (!pfvf->tx_chan_cnt)
+ goto chk_vfs;
+
+ info->is_vf = 0;
+ info->pcifunc = pcifunc;
+ info->rx_chan_base = pfvf->rx_chan_base;
+ info->rx_chan_cnt = pfvf->rx_chan_cnt;
+ info->tx_chan_base = pfvf->tx_chan_base;
+ info->tx_chan_cnt = pfvf->tx_chan_cnt;
+ info->tx_link = nix_get_tx_link(rvu, pcifunc);
+ if (is_sdp_pfvf(rvu, pcifunc))
+ info->is_sdp = 1;
+
+ rvu_iface_get_qcnts(rvu, pfvf, info);
+
+ if (pfvf->nix_blkaddr == BLKADDR_NIX0)
+ info->nix = 0;
+ else
+ info->nix = 1;
+
+ info++;
+ tot++;
+
+chk_vfs:
+ for (vf = 0; vf < numvfs; vf++) {
+ if (tot >= IFACE_MAX) {
+ truncated = true;
+ goto done;
+ }
+
+ pcifunc = rvu_make_pcifunc(rvu->pdev, pf, vf + 1);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+ if (!pfvf->tx_chan_cnt)
+ continue;
+
+ info->is_vf = 1;
+ info->pcifunc = pcifunc;
+ info->rx_chan_base = pfvf->rx_chan_base;
+ info->rx_chan_cnt = pfvf->rx_chan_cnt;
+ info->tx_chan_base = pfvf->tx_chan_base;
+ info->tx_chan_cnt = pfvf->tx_chan_cnt;
+ info->tx_link = nix_get_tx_link(rvu, pcifunc);
+ if (is_sdp_pfvf(rvu, pcifunc))
+ info->is_sdp = 1;
+
+ rvu_iface_get_qcnts(rvu, pfvf, info);
+
+ if (pfvf->nix_blkaddr == BLKADDR_NIX0)
+ info->nix = 0;
+ else
+ info->nix = 1;
+
+ info++;
+
+ tot++;
+ }
+ }
+done:
+ mutex_unlock(&rvu->rsrc_lock);
+
+ rsp->cnt = tot;
+ rsp->truncated = truncated;
+
+ return 0;
+}
+
int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req,
struct free_rsrcs_rsp *rsp)
{
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index fb4870cd18e9..d3408b86f0de 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -24,6 +24,7 @@
#define PCI_DEVID_OCTEONTX2_RVU_AF 0xA065
#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8
#define PCI_DEVID_OCTEONTX2_LBK 0xA061
+#define PCI_DEVID_PAN_RVU 0xA0E2
/* Subsystem Device ID */
#define PCI_SUBSYS_DEVID_98XX 0xB100
@@ -1161,6 +1162,7 @@ void rvu_program_channels(struct rvu *rvu);
/* CN10K NIX */
void rvu_nix_block_cn10k_init(struct rvu *rvu, struct nix_hw *nix_hw);
+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);
/* CN10K RVU - LMT*/
void rvu_reset_lmt_map_tbl(struct rvu *rvu, u16 pcifunc);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index d80d2c00bd84..354c575d4710 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -32,7 +32,6 @@ static int nix_free_all_bandprof(struct rvu *rvu, u16 pcifunc);
static void nix_clear_ratelimit_aggr(struct rvu *rvu, struct nix_hw *nix_hw,
u32 leaf_prof);
static const char *nix_get_ctx_name(int ctype);
-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);
enum mc_tbl_sz {
MC_TBL_SZ_256,
@@ -337,6 +336,28 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
return true;
}
+static void nix_interface_set_chan(struct rvu *rvu, struct rvu_pfvf *pfvf,
+ u16 rx_chan_base, u8 rx_chan_cnt,
+ u16 tx_chan_base, u8 tx_chan_cnt)
+{
+ mutex_lock(&rvu->rsrc_lock);
+ pfvf->rx_chan_base = rx_chan_base;
+ pfvf->rx_chan_cnt = rx_chan_cnt;
+ pfvf->tx_chan_base = tx_chan_base;
+ pfvf->tx_chan_cnt = tx_chan_cnt;
+ mutex_unlock(&rvu->rsrc_lock);
+}
+
+static void nix_interface_clear_chan(struct rvu *rvu, struct rvu_pfvf *pfvf)
+{
+ mutex_lock(&rvu->rsrc_lock);
+ pfvf->rx_chan_base = 0;
+ pfvf->rx_chan_cnt = 0;
+ pfvf->tx_chan_base = 0;
+ pfvf->tx_chan_cnt = 0;
+ mutex_unlock(&rvu->rsrc_lock);
+}
+
static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
struct nix_lf_alloc_rsp *rsp, bool loop)
{
@@ -365,10 +386,9 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
"PF_Func 0x%x: Invalid pkind\n", pcifunc);
return -EINVAL;
}
- pfvf->rx_chan_base = rvu_nix_chan_cgx(rvu, cgx_id, lmac_id, 0);
- pfvf->tx_chan_base = pfvf->rx_chan_base;
- pfvf->rx_chan_cnt = 1;
- pfvf->tx_chan_cnt = 1;
+ nix_interface_set_chan(rvu, pfvf,
+ rvu_nix_chan_cgx(rvu, cgx_id, lmac_id, 0), 1,
+ rvu_nix_chan_cgx(rvu, cgx_id, lmac_id, 0), 1);
rsp->tx_link = cgx_id * hw->lmac_per_cgx + lmac_id;
if (rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, pkind))
@@ -410,12 +430,11 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
* 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;
+ nix_interface_set_chan(rvu, pfvf,
+ rvu_nix_chan_lbk(rvu, lbkid, vf), 1,
+ vf & 0x1 ?
+ rvu_nix_chan_lbk(rvu, lbkid, vf - 1) :
+ rvu_nix_chan_lbk(rvu, lbkid, vf + 1), 1);
rsp->tx_link = hw->cgx_links + lbkid;
pfvf->lbkid = lbkid;
rvu_npc_set_pkind(rvu, NPC_RX_LBK_PKIND, pfvf);
@@ -452,10 +471,8 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
req_chan_cnt = sdp_info->num_pf_rings;
}
- pfvf->rx_chan_base = req_chan_base;
- pfvf->rx_chan_cnt = req_chan_cnt;
- pfvf->tx_chan_base = pfvf->rx_chan_base;
- pfvf->tx_chan_cnt = pfvf->rx_chan_cnt;
+ nix_interface_set_chan(rvu, pfvf, req_chan_base, req_chan_cnt,
+ req_chan_base, req_chan_cnt);
rsp->tx_link = hw->cgx_links + hw->lbk_links;
rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
@@ -912,33 +929,114 @@ static void nix_setup_lso(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
nix_hw->lso.in_use++;
}
+static int nix_qctx_assign(struct rvu *rvu, int blkaddr, struct qmem **ctx,
+ unsigned long **bmap, struct qmem *new_ctx,
+ unsigned long *new_bmap)
+{
+ struct admin_queue *aq = rvu->hw->block[blkaddr].aq;
+ unsigned long flags;
+
+ if (!aq) {
+ WARN_ON_ONCE(1);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&aq->lock, flags);
+ *ctx = new_ctx;
+ *bmap = new_bmap;
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ return 0;
+}
+
+static int nix_ctx_assign(struct rvu *rvu, int blkaddr, struct qmem **ctx,
+ struct qmem *new_ctx)
+{
+ struct admin_queue *aq = rvu->hw->block[blkaddr].aq;
+ unsigned long flags;
+
+ if (!aq) {
+ WARN_ON_ONCE(1);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&aq->lock, flags);
+ *ctx = new_ctx;
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ return 0;
+}
+
static void 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);
+ struct admin_queue *aq = rvu->hw->block[pfvf->nix_blkaddr].aq;
+ unsigned long *rq_bmap, *sq_bmap, *cq_bmap;
+ struct qmem *rq_ctx, *sq_ctx, *cq_ctx;
+ struct qmem *rss_ctx, *nix_qints_ctx, *cq_ints_ctx;
+ unsigned long flags;
+
+ if (!aq) {
+ WARN_ON_ONCE(1);
+ rq_bmap = pfvf->rq_bmap;
+ sq_bmap = pfvf->sq_bmap;
+ cq_bmap = pfvf->cq_bmap;
+ rq_ctx = pfvf->rq_ctx;
+ sq_ctx = pfvf->sq_ctx;
+ cq_ctx = pfvf->cq_ctx;
+ rss_ctx = pfvf->rss_ctx;
+ nix_qints_ctx = pfvf->nix_qints_ctx;
+ cq_ints_ctx = pfvf->cq_ints_ctx;
+
+ pfvf->rq_bmap = NULL;
+ pfvf->sq_bmap = NULL;
+ pfvf->cq_bmap = NULL;
+ pfvf->rq_ctx = NULL;
+ pfvf->sq_ctx = NULL;
+ pfvf->cq_ctx = NULL;
+ pfvf->rss_ctx = NULL;
+ pfvf->nix_qints_ctx = NULL;
+ pfvf->cq_ints_ctx = NULL;
+ goto free_ctx;
+ }
+
+ spin_lock_irqsave(&aq->lock, flags);
+ rq_bmap = pfvf->rq_bmap;
+ sq_bmap = pfvf->sq_bmap;
+ cq_bmap = pfvf->cq_bmap;
+ rq_ctx = pfvf->rq_ctx;
+ sq_ctx = pfvf->sq_ctx;
+ cq_ctx = pfvf->cq_ctx;
+ rss_ctx = pfvf->rss_ctx;
+ nix_qints_ctx = pfvf->nix_qints_ctx;
+ cq_ints_ctx = pfvf->cq_ints_ctx;
pfvf->rq_bmap = NULL;
- pfvf->cq_bmap = NULL;
pfvf->sq_bmap = NULL;
+ pfvf->cq_bmap = NULL;
pfvf->rq_ctx = NULL;
pfvf->sq_ctx = NULL;
pfvf->cq_ctx = NULL;
pfvf->rss_ctx = NULL;
pfvf->nix_qints_ctx = NULL;
pfvf->cq_ints_ctx = NULL;
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+free_ctx:
+ kfree(rq_bmap);
+ kfree(sq_bmap);
+ kfree(cq_bmap);
+ if (rq_ctx)
+ qmem_free(rvu->dev, rq_ctx);
+ if (sq_ctx)
+ qmem_free(rvu->dev, sq_ctx);
+ if (cq_ctx)
+ qmem_free(rvu->dev, cq_ctx);
+ if (rss_ctx)
+ qmem_free(rvu->dev, rss_ctx);
+ if (nix_qints_ctx)
+ qmem_free(rvu->dev, nix_qints_ctx);
+ if (cq_ints_ctx)
+ qmem_free(rvu->dev, cq_ints_ctx);
}
static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
@@ -946,6 +1044,7 @@ static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
int rss_sz, int rss_grps, int hwctx_size,
u64 way_mask, bool tag_lsb_as_adder)
{
+ struct qmem *rss_ctx;
int err, grp, num_indices;
u64 val;
@@ -955,12 +1054,14 @@ static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
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)
+ err = qmem_alloc(rvu->dev, &rss_ctx, num_indices, hwctx_size);
+ if (err) {
+ qmem_free(rvu->dev, rss_ctx);
return err;
+ }
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_BASE(nixlf),
- (u64)pfvf->rss_ctx->iova);
+ (u64)rss_ctx->iova);
/* Config full RSS table size, enable RSS and caching */
val = BIT_ULL(36) | BIT_ULL(4) | way_mask << 20 |
@@ -974,6 +1075,13 @@ static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
for (grp = 0; grp < rss_grps; grp++)
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_GRPX(nixlf, grp),
((ilog2(rss_sz) - 1) << 16) | (rss_sz * grp));
+
+ err = nix_ctx_assign(rvu, blkaddr, &pfvf->rss_ctx, rss_ctx);
+ if (err) {
+ qmem_free(rvu->dev, rss_ctx);
+ return err;
+ }
+
return 0;
}
@@ -1514,6 +1622,9 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
struct nix_lf_alloc_rsp *rsp)
{
int nixlf, qints, hwctx_size, intf, rc = 0, pf;
+ unsigned long *rq_bmap, *sq_bmap, *cq_bmap;
+ struct qmem *cq_ints_ctx, *nix_qints_ctx;
+ struct qmem *rq_ctx, *sq_ctx, *cq_ctx;
u16 bcast, mcast, promisc, ucast;
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = req->hdr.pcifunc;
@@ -1584,59 +1695,92 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
/* Alloc NIX RQ HW context memory and config the base */
hwctx_size = 1UL << ((ctx_cfg >> 4) & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &rq_ctx, req->rq_cnt, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, rq_ctx);
goto free_mem;
+ }
- pfvf->rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL);
- if (!pfvf->rq_bmap) {
+ rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL);
+ if (!rq_bmap) {
+ qmem_free(rvu->dev, rq_ctx);
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_BASE(nixlf),
- (u64)pfvf->rq_ctx->iova);
+ (u64)rq_ctx->iova);
/* Set caching and queue count in HW */
cfg = BIT_ULL(36) | (req->rq_cnt - 1) | req->way_mask << 20;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_CFG(nixlf), cfg);
+ rc = nix_qctx_assign(rvu, blkaddr, &pfvf->rq_ctx, &pfvf->rq_bmap,
+ rq_ctx, rq_bmap);
+ if (rc) {
+ kfree(rq_bmap);
+ qmem_free(rvu->dev, rq_ctx);
+ goto free_mem;
+ }
+
/* Alloc NIX SQ HW context memory and config the base */
hwctx_size = 1UL << (ctx_cfg & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &sq_ctx, req->sq_cnt, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, sq_ctx);
goto free_mem;
+ }
- pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
- if (!pfvf->sq_bmap) {
+ sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
+ if (!sq_bmap) {
+ qmem_free(rvu->dev, sq_ctx);
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_BASE(nixlf),
- (u64)pfvf->sq_ctx->iova);
+ (u64)sq_ctx->iova);
cfg = BIT_ULL(36) | (req->sq_cnt - 1) | req->way_mask << 20;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_CFG(nixlf), cfg);
+ rc = nix_qctx_assign(rvu, blkaddr, &pfvf->sq_ctx, &pfvf->sq_bmap,
+ sq_ctx, sq_bmap);
+ if (rc) {
+ kfree(sq_bmap);
+ qmem_free(rvu->dev, sq_ctx);
+ goto free_mem;
+ }
+
/* Alloc NIX CQ HW context memory and config the base */
hwctx_size = 1UL << ((ctx_cfg >> 8) & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &cq_ctx, req->cq_cnt, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, cq_ctx);
goto free_mem;
+ }
- pfvf->cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL);
- if (!pfvf->cq_bmap) {
+ cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL);
+ if (!cq_bmap) {
+ qmem_free(rvu->dev, cq_ctx);
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_BASE(nixlf),
- (u64)pfvf->cq_ctx->iova);
+ (u64)cq_ctx->iova);
cfg = BIT_ULL(36) | (req->cq_cnt - 1) | req->way_mask << 20;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_CFG(nixlf), cfg);
+ rc = nix_qctx_assign(rvu, blkaddr, &pfvf->cq_ctx, &pfvf->cq_bmap,
+ cq_ctx, cq_bmap);
+ if (rc) {
+ kfree(cq_bmap);
+ qmem_free(rvu->dev, cq_ctx);
+ goto free_mem;
+ }
+
/* Initialize receive side scaling (RSS) */
hwctx_size = 1UL << ((ctx_cfg >> 12) & 0xF);
rc = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz,
@@ -1649,29 +1793,45 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
qints = (cfg >> 24) & 0xFFF;
hwctx_size = 1UL << ((ctx_cfg >> 24) & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &cq_ints_ctx, qints, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, cq_ints_ctx);
goto free_mem;
+ }
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_BASE(nixlf),
- (u64)pfvf->cq_ints_ctx->iova);
+ (u64)cq_ints_ctx->iova);
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_CFG(nixlf),
BIT_ULL(36) | req->way_mask << 20);
+ rc = nix_ctx_assign(rvu, blkaddr, &pfvf->cq_ints_ctx, cq_ints_ctx);
+ if (rc) {
+ qmem_free(rvu->dev, cq_ints_ctx);
+ goto free_mem;
+ }
+
/* Alloc memory for QINT's HW contexts */
cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
qints = (cfg >> 12) & 0xFFF;
hwctx_size = 1UL << ((ctx_cfg >> 20) & 0xF);
- rc = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size);
- if (rc)
+ rc = qmem_alloc(rvu->dev, &nix_qints_ctx, qints, hwctx_size);
+ if (rc) {
+ qmem_free(rvu->dev, nix_qints_ctx);
goto free_mem;
+ }
rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_BASE(nixlf),
- (u64)pfvf->nix_qints_ctx->iova);
+ (u64)nix_qints_ctx->iova);
rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_CFG(nixlf),
BIT_ULL(36) | req->way_mask << 20);
+ rc = nix_ctx_assign(rvu, blkaddr, &pfvf->nix_qints_ctx, nix_qints_ctx);
+ if (rc) {
+ qmem_free(rvu->dev, nix_qints_ctx);
+ goto free_mem;
+ }
+
/* Setup VLANX TPID's.
* Use VLAN1 for 802.1Q
* and VLAN0 for 802.1AD.
@@ -1711,8 +1871,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
}
if (is_rep_dev(rvu, pcifunc)) {
- pfvf->tx_chan_base = RVU_SWITCH_LBK_CHAN;
- pfvf->tx_chan_cnt = 1;
+ nix_interface_set_chan(rvu, pfvf, 0, 0,
+ RVU_SWITCH_LBK_CHAN, 1);
goto exit;
}
@@ -1816,6 +1976,8 @@ int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct nix_lf_free_req *req,
nix_interface_deinit(rvu, pcifunc, nixlf);
free_lf:
+ nix_interface_clear_chan(rvu, pfvf);
+
/* Reset this NIX LF */
err = rvu_lf_reset(rvu, block, nixlf);
if (err) {
@@ -2109,15 +2271,22 @@ static void nix_clear_tx_xoff(struct rvu *rvu, int blkaddr,
rvu_write64(rvu, blkaddr, reg, 0x0);
}
-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
{
- struct rvu_hwinfo *hw = rvu->hw;
int pf = rvu_get_pf(rvu->pdev, pcifunc);
+ struct rvu_hwinfo *hw = rvu->hw;
+ struct rvu_pfvf *pfvf;
u8 cgx_id = 0, lmac_id = 0;
- if (is_lbk_vf(rvu, pcifunc)) {/* LBK links */
+ if (is_rep_dev(rvu, pcifunc))
return hw->cgx_links;
- } else if (is_pf_cgxmapped(rvu, pf)) {
+
+ if (is_lbk_vf(rvu, pcifunc)) {
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+ return hw->cgx_links + pfvf->lbkid;
+ }
+
+ if (is_pf_cgxmapped(rvu, pf)) {
rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
return (cgx_id * hw->lmac_per_cgx) + lmac_id;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index c34f8d86cc8a..25101541d20b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -3544,6 +3544,53 @@ int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
return rc;
}
+int rvu_mbox_handler_npc_flow_del_n_free(struct rvu *rvu,
+ struct npc_flow_del_n_free_req *mreq,
+ struct msg_rsp *rsp)
+{
+ struct npc_mcam_free_entry_req sreq = { 0 };
+ struct npc_delete_flow_req dreq = { 0 };
+ struct npc_delete_flow_rsp drsp = { 0 };
+ u16 entry[256];
+ int ret = 0, i;
+ bool err = false;
+ u16 cnt;
+
+ sreq.hdr.pcifunc = mreq->hdr.pcifunc;
+ dreq.hdr.pcifunc = mreq->hdr.pcifunc;
+
+ cnt = mreq->cnt;
+ if (!cnt || cnt > 256) {
+ dev_err_ratelimited(rvu->dev, "Invalid cnt=%u\n", cnt);
+ return -EINVAL;
+ }
+
+ /* Snapshot shared mailbox memory before processing the request. */
+ memcpy(entry, mreq->entry, cnt * sizeof(entry[0]));
+
+ for (i = 0; i < cnt; i++) {
+ dreq.entry = entry[i];
+ ret = rvu_mbox_handler_npc_delete_flow(rvu, &dreq, &drsp);
+ if (ret) {
+ dev_err_ratelimited(rvu->dev,
+ "delete flow error for i=%d entry=%d\n",
+ i, entry[i]);
+ err = true;
+ }
+
+ sreq.entry = entry[i];
+ ret = rvu_mbox_handler_npc_mcam_free_entry(rvu, &sreq, rsp);
+ if (ret) {
+ dev_err_ratelimited(rvu->dev,
+ "free entry error for i=%d entry=%d\n",
+ i, entry[i]);
+ err = true;
+ }
+ }
+
+ return err ? -EINVAL : 0;
+}
+
int rvu_mbox_handler_npc_mcam_read_entry(struct rvu *rvu,
struct npc_mcam_read_entry_req *req,
struct npc_mcam_read_entry_rsp *rsp)
@@ -4443,6 +4490,83 @@ int rvu_mbox_handler_npc_mcam_entry_stats(struct rvu *rvu,
return 0;
}
+int rvu_mbox_handler_npc_mcam_mul_stats(struct rvu *rvu,
+ struct npc_mcam_get_mul_stats_req *req,
+ struct npc_mcam_get_mul_stats_rsp *rsp)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+ u16 req_cnt, index, cntr, mcam_entry;
+ u16 pcifunc = req->hdr.pcifunc;
+ int blkaddr, cnt = 0, i;
+ u16 entry[256];
+ u64 regval;
+ u32 bank;
+
+ rsp->cnt = 0;
+ memset(rsp->rsvd, 0, sizeof(rsp->rsvd));
+ memset(rsp->stat, 0, sizeof(rsp->stat));
+
+ req_cnt = req->cnt;
+ if (!req_cnt || req_cnt > 256) {
+ dev_err_ratelimited(rvu->dev, "%s invalid request cnt=%u\n",
+ __func__, req_cnt);
+ return -EINVAL;
+ }
+
+ /* Snapshot shared mailbox memory before processing the request. */
+ memcpy(entry, req->entry, req_cnt * sizeof(entry[0]));
+
+ blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+ if (blkaddr < 0)
+ return NPC_MCAM_INVALID_REQ;
+
+ mutex_lock(&mcam->lock);
+
+ for (i = 0; i < req_cnt; i++) {
+ mcam_entry = npc_cn20k_vidx2idx(entry[i]);
+
+ if (npc_mcam_verify_entry(mcam, pcifunc, mcam_entry)) {
+ mutex_unlock(&mcam->lock);
+ dev_err_ratelimited(rvu->dev, "%s invalid mcam index=%d\n",
+ __func__, entry[i]);
+ memset(rsp->stat, 0, sizeof(rsp->stat));
+ rsp->cnt = 0;
+ return -EINVAL;
+ }
+
+ index = mcam_entry & (mcam->banksize - 1);
+ bank = npc_get_bank(mcam, mcam_entry);
+
+ if (is_cn20k(rvu->pdev)) {
+ regval = rvu_read64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index,
+ bank));
+ rsp->stat[cnt] = regval;
+ cnt++;
+ continue;
+ }
+
+ /* read MCAM entry STAT_ACT register */
+ regval = rvu_read64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank));
+
+ if (!(regval & rvu->hw->npc_stat_ena)) {
+ rsp->stat[cnt] = 0;
+ cnt++;
+ continue;
+ }
+
+ cntr = regval & 0x1FF;
+
+ rsp->stat[cnt] = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(cntr));
+ rsp->stat[cnt] &= BIT_ULL(48) - 1;
+ cnt++;
+ }
+
+ rsp->cnt = cnt;
+ mutex_unlock(&mcam->lock);
+ return 0;
+}
+
void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf)
{
struct npc_mcam *mcam = &rvu->hw->mcam;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
index d422bdd5e8f8..30e55d44a7c4 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
@@ -1931,6 +1931,20 @@ static int npc_delete_flow(struct rvu *rvu, struct rvu_npc_mcam_rule *rule,
return rvu_mbox_handler_npc_mcam_dis_entry(rvu, &dis_req, &dis_rsp);
}
+int rvu_mbox_handler_npc_mcam_get_features(struct rvu *rvu,
+ struct msg_req *req,
+ struct npc_mcam_get_features_rsp *rsp)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+
+ mutex_lock(&mcam->lock);
+ rsp->rx_features = mcam->rx_features;
+ rsp->tx_features = mcam->tx_features;
+ mutex_unlock(&mcam->lock);
+
+ return 0;
+}
+
int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,
struct npc_delete_flow_req *req,
struct npc_delete_flow_rsp *rsp)
@@ -1939,7 +1953,7 @@ int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,
struct rvu_npc_mcam_rule *iter, *tmp;
u16 pcifunc = req->hdr.pcifunc;
struct list_head del_list;
- int blkaddr;
+ int blkaddr, err;
req->entry = npc_cn20k_vidx2idx(req->entry);
req->start = npc_cn20k_vidx2idx(req->start);
@@ -1970,17 +1984,20 @@ int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,
}
mutex_unlock(&mcam->lock);
+ err = 0;
list_for_each_entry_safe(iter, tmp, &del_list, list) {
u16 entry = iter->entry;
/* clear the mcam entry target pcifunc */
mcam->entry2target_pffunc[entry] = 0x0;
- if (npc_delete_flow(rvu, iter, pcifunc))
+ if (npc_delete_flow(rvu, iter, pcifunc)) {
dev_err(rvu->dev, "rule deletion failed for entry:%u",
entry);
+ err = -EINVAL;
+ }
}
- return 0;
+ return err;
}
static int npc_update_dmac_value(struct rvu *rvu, int npcblkaddr,
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-28 3:01 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-24 2:44 [PATCH v2 net-next] octeontx2-af: Add couple of mailbox support Ratheesh Kannoth
2026-09-28 3:01 ` netdev-bot+sashiko
-- strict thread matches above, loose matches on Subject: below --
2026-09-23 3:28 Ratheesh Kannoth
2026-09-23 3:59 ` Ratheesh Kannoth
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®