[PATCH net v2] octeontx2-af: Fix Rep link state sync up with PF/VFs

From: nshettyj

Date: Thu Aug 27 2026 - 04:28:44 EST


From: Geetha sowjanya <gakula@xxxxxxxxxxx>

Sync representor link state with PF/VFs and move rep event workqueue
init to rvu_mbox_handler_get_rep_cnt().

Fixes: b8fea84a0468 ("octeontx2-pf: Add support to sync link state between representor and VFs")
Signed-off-by: Geetha sowjanya <gakula@xxxxxxxxxxx>
Signed-off-by: Nitin Shetty J <nshettyj@xxxxxxxxxxx>
---
v2: Reject REP event notifications before workqueue setup and
validate PF/VF state event pcifuncs.
v2: Make REP map initialization atomic by using a temporary map,
handling zero-REP cases, and rolling back on workqueue allocation
failure.
v2: Use an unbound REP event workqueue.
v2: Serialize LBK link TL2 configuration with rsrc_lock.
---
.../net/ethernet/marvell/octeontx2/af/rvu.c | 4 +
.../ethernet/marvell/octeontx2/af/rvu_rep.c | 113 ++++++++++++------
.../marvell/octeontx2/af/rvu_switch.c | 2 +
.../net/ethernet/marvell/octeontx2/nic/rep.c | 6 +
4 files changed, 86 insertions(+), 39 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index 74c041ab5280..f484e362cf57 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -3716,6 +3716,10 @@ static void rvu_remove(struct pci_dev *pdev)
rvu_unregister_dl(rvu);
rvu_unregister_interrupts(rvu);
rvu_flr_wq_destroy(rvu);
+ if (rvu->rep_evt_wq) {
+ destroy_workqueue(rvu->rep_evt_wq);
+ rvu->rep_evt_wq = NULL;
+ }
rvu_cgx_exit(rvu);
rvu_fwdata_exit(rvu);
rvu_mcs_exit(rvu);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
index a2781e0f504e..ef8d30eeeafd 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
@@ -44,6 +44,8 @@ static int rvu_rep_up_notify(struct rvu *rvu, struct rep_event *event)
if (event->event & RVU_EVENT_MAC_ADDR_CHANGE)
ether_addr_copy(pfvf->mac_addr, event->evt_data.mac);

+ if (event->event & RVU_EVENT_PFVF_STATE)
+ pf = rvu_get_pf(rvu->pdev, event->hdr.pcifunc);
mutex_lock(&rvu->mbox_lock);
msg = otx2_mbox_alloc_msg_rep_event_up_notify(rvu, pf);
if (!msg) {
@@ -53,6 +55,10 @@ static int rvu_rep_up_notify(struct rvu *rvu, struct rep_event *event)

msg->hdr.pcifunc = event->pcifunc;
msg->event = event->event;
+ msg->pcifunc = event->pcifunc;
+
+ if (event->event & RVU_EVENT_PFVF_STATE)
+ msg->hdr.pcifunc = event->hdr.pcifunc;

memcpy(&msg->evt_data, &event->evt_data, sizeof(struct rep_evt_data));

@@ -97,6 +103,9 @@ int rvu_mbox_handler_rep_event_notify(struct rvu *rvu, struct rep_event *req,
{
struct rep_evtq_ent *qentry;

+ if (!rvu->rep_evt_wq)
+ return -EINVAL;
+
/* The mailbox dispatcher normalises only the header pcifunc; the
* nested struct rep_event::pcifunc body field is sender-controlled
* and is later used by rvu_rep_up_notify() to index rvu->pf[] /
@@ -105,6 +114,10 @@ int rvu_mbox_handler_rep_event_notify(struct rvu *rvu, struct rep_event *req,
if (!is_pf_func_valid(rvu, req->pcifunc))
return -EINVAL;

+ if ((req->event & RVU_EVENT_PFVF_STATE) &&
+ rvu_get_pf(rvu->pdev, req->hdr.pcifunc) >= rvu->hw->total_pfs)
+ return -EINVAL;
+
qentry = kmalloc_obj(*qentry, GFP_ATOMIC);
if (!qentry)
return -ENOMEM;
@@ -119,31 +132,17 @@ int rvu_mbox_handler_rep_event_notify(struct rvu *rvu, struct rep_event *req,

int rvu_rep_notify_pfvf_state(struct rvu *rvu, u16 pcifunc, bool enable)
{
- struct rep_event *req;
- int pf;
+ struct rep_event req = { 0 };
+ struct msg_rsp rsp;

if (!is_pf_cgxmapped(rvu, rvu_get_pf(rvu->pdev, pcifunc)))
return 0;

- pf = rvu_get_pf(rvu->pdev, rvu->rep_pcifunc);
-
- mutex_lock(&rvu->mbox_lock);
- req = otx2_mbox_alloc_msg_rep_event_up_notify(rvu, pf);
- if (!req) {
- mutex_unlock(&rvu->mbox_lock);
- return -ENOMEM;
- }
-
- req->hdr.pcifunc = rvu->rep_pcifunc;
- req->event |= RVU_EVENT_PFVF_STATE;
- req->pcifunc = pcifunc;
- req->evt_data.vf_state = enable;
-
- otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, pf);
- otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, pf);
-
- mutex_unlock(&rvu->mbox_lock);
- return 0;
+ req.hdr.pcifunc = rvu->rep_pcifunc;
+ req.event = RVU_EVENT_PFVF_STATE;
+ req.pcifunc = pcifunc;
+ req.evt_data.vf_state = enable;
+ return rvu_mbox_handler_rep_event_notify(rvu, &req, &rsp);
}

#define RVU_LF_RX_STATS(reg) \
@@ -325,6 +324,7 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu)
u16 start = rswitch->start_entry;
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc, entry = 0;
+ struct rvu_pfvf *pfvf;
int pf, vf, numvfs;
int err, nixlf, i;
u8 rep;
@@ -334,7 +334,10 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu)
continue;

pcifunc = rvu_make_pcifunc(rvu->pdev, pf, 0);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
rvu_get_nix_blkaddr(rvu, pcifunc);
+ if (test_bit(NIXLF_INITIALIZED, &pfvf->flags))
+ rvu_switch_enable_lbk_link(rvu, pcifunc, true);
rep = true;
for (i = 0; i < 2; i++) {
err = rvu_rep_install_rx_rule(rvu, pcifunc,
@@ -354,6 +357,9 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu)
rvu_get_pf_numvfs(rvu, pf, &numvfs, NULL);
for (vf = 0; vf < numvfs; vf++) {
pcifunc = rvu_make_pcifunc(rvu->pdev, pf, vf + 1);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+ if (test_bit(NIXLF_INITIALIZED, &pfvf->flags))
+ rvu_switch_enable_lbk_link(rvu, pcifunc, true);
rvu_get_nix_blkaddr(rvu, pcifunc);

/* Skip installimg rules if nixlf is not attached */
@@ -379,16 +385,6 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu)
}
}
}
-
- /* Initialize the wq for handling REP events */
- spin_lock_init(&rvu->rep_evtq_lock);
- INIT_LIST_HEAD(&rvu->rep_evtq_head);
- INIT_WORK(&rvu->rep_evt_work, rvu_rep_wq_handler);
- rvu->rep_evt_wq = alloc_workqueue("rep_evt_wq", WQ_PERCPU, 0);
- if (!rvu->rep_evt_wq) {
- dev_err(rvu->dev, "REP workqueue allocation failed\n");
- return -ENOMEM;
- }
return 0;
}

@@ -443,35 +439,74 @@ int rvu_mbox_handler_esw_cfg(struct rvu *rvu, struct esw_cfg_req *req,
return 0;
}

+static int rvu_rep_get_rep_map(struct rvu *rvu, struct msg_req *req,
+ struct get_rep_cnt_rsp *rsp)
+{
+ int rep;
+
+ rvu->rep_pcifunc = req->hdr.pcifunc;
+ rsp->rep_cnt = rvu->rep_cnt;
+ for (rep = 0; rep < rvu->rep_cnt; rep++)
+ rsp->rep_pf_map[rep] = rvu->rep2pfvf_map[rep];
+
+ return 0;
+}
+
int rvu_mbox_handler_get_rep_cnt(struct rvu *rvu, struct msg_req *req,
struct get_rep_cnt_rsp *rsp)
{
int pf, vf, numvfs, hwvf, rep = 0;
u16 pcifunc;
+ u16 *map;
+
+ if (rvu->rep2pfvf_map)
+ return rvu_rep_get_rep_map(rvu, req, rsp);

rvu->rep_pcifunc = req->hdr.pcifunc;
rsp->rep_cnt = rvu->cgx_mapped_pfs + rvu->cgx_mapped_vfs;
- rvu->rep_cnt = rsp->rep_cnt;

- rvu->rep2pfvf_map = devm_kzalloc(rvu->dev, rvu->rep_cnt *
- sizeof(u16), GFP_KERNEL);
- if (!rvu->rep2pfvf_map)
+ /* Allocate at least one element so the map pointer is always non-NULL
+ * once published, making the above guard unconditionally reliable.
+ */
+ map = devm_kzalloc(rvu->dev, (rsp->rep_cnt ?: 1) * sizeof(u16),
+ GFP_KERNEL);
+ if (!map)
return -ENOMEM;

+ rvu->rep_cnt = rsp->rep_cnt;
+
for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
if (!is_pf_cgxmapped(rvu, pf))
continue;
pcifunc = rvu_make_pcifunc(rvu->pdev, pf, 0);
- rvu->rep2pfvf_map[rep] = pcifunc;
+ map[rep] = pcifunc;
rsp->rep_pf_map[rep] = pcifunc;
rep++;
rvu_get_pf_numvfs(rvu, pf, &numvfs, &hwvf);
for (vf = 0; vf < numvfs; vf++) {
- rvu->rep2pfvf_map[rep] = pcifunc |
- ((vf + 1) & RVU_PFVF_FUNC_MASK);
- rsp->rep_pf_map[rep] = rvu->rep2pfvf_map[rep];
+ map[rep] = pcifunc | ((vf + 1) & RVU_PFVF_FUNC_MASK);
+ rsp->rep_pf_map[rep] = map[rep];
rep++;
}
}
+
+ if (!rvu->rep_cnt) {
+ rvu->rep2pfvf_map = map;
+ return 0;
+ }
+
+ /* Initialize the wq for handling REP events */
+ spin_lock_init(&rvu->rep_evtq_lock);
+ INIT_LIST_HEAD(&rvu->rep_evtq_head);
+ INIT_WORK(&rvu->rep_evt_work, rvu_rep_wq_handler);
+ rvu->rep_evt_wq = alloc_workqueue("rep_evt_wq", WQ_UNBOUND, 0);
+ if (!rvu->rep_evt_wq) {
+ dev_err(rvu->dev, "REP workqueue allocation failed\n");
+ devm_kfree(rvu->dev, map);
+ rvu->rep_cnt = 0;
+ return -ENOMEM;
+ }
+
+ rvu->rep2pfvf_map = map;
return 0;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_switch.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_switch.c
index 49ce38685a7e..6f1330ae8d90 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_switch.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_switch.c
@@ -15,8 +15,10 @@ void rvu_switch_enable_lbk_link(struct rvu *rvu, u16 pcifunc, bool enable)

nix_hw = get_nix_hw(rvu->hw, pfvf->nix_blkaddr);
/* Enable LBK links with channel 63 for TX MCAM rule */
+ mutex_lock(&rvu->rsrc_lock);
rvu_nix_tx_tl2_cfg(rvu, pfvf->nix_blkaddr, pcifunc,
&nix_hw->txsch[NIX_TXSCH_LVL_TL2], enable);
+ mutex_unlock(&rvu->rsrc_lock);
}

static int rvu_switch_install_rx_rule(struct rvu *rvu, u16 pcifunc,
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
index 0f5d5642d3f7..3f89a2c04b2b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
@@ -459,6 +459,9 @@ static int rvu_rep_open(struct net_device *dev)
netif_carrier_on(dev);
netif_tx_start_all_queues(dev);

+ if (rep->pcifunc & RVU_PFVF_FUNC_MASK)
+ return 0;
+
evt.event = RVU_EVENT_PORT_STATE;
evt.evt_data.port_state = 1;
evt.pcifunc = rep->pcifunc;
@@ -478,6 +481,9 @@ static int rvu_rep_stop(struct net_device *dev)
netif_carrier_off(dev);
netif_tx_disable(dev);

+ if (rep->pcifunc & RVU_PFVF_FUNC_MASK)
+ return 0;
+
evt.event = RVU_EVENT_PORT_STATE;
evt.pcifunc = rep->pcifunc;
rvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, &evt);
--
2.48.1