RE: [PATCH net] octeontx2-af: poll for TX link credits before link mode change
From: Jagielski, Jedrzej
Date: Tue Aug 18 2026 - 03:46:13 EST
From: Ratheesh Kannoth <rkannoth@xxxxxxxxxxx>
Sent: Tuesday, August 18, 2026 5:37 AM
Hi Naveen, Ratheesh
>From: Naveen Mamindlapalli <naveenm@xxxxxxxxxxx>
>
>When a CGX/RPM link mode change is performed while traffic is flowing,
>the link is temporarily disabled. NIX TX link credits can drop below
>zero during this window and are not restored when the link comes back,
>leading to a TX hang.
>
>Set TL1 SW_XOFF before initiating a link mode change and poll until TX
>link credits return to their original value. Clear TL1 SW_XOFF when the
>link comes back up.
>
>Fixes: 56b6d5398613 ("octeontx2-af: Physical link configuration support")
>Signed-off-by: Naveen Mamindlapalli <naveenm@xxxxxxxxxxx>
>Signed-off-by: Ratheesh Kannoth <rkannoth@xxxxxxxxxxx>
>---
> .../net/ethernet/marvell/octeontx2/af/rvu.h | 2 +
> .../ethernet/marvell/octeontx2/af/rvu_cgx.c | 19 +++++++-
> .../ethernet/marvell/octeontx2/af/rvu_nix.c | 48 +++++++++++++++++++
> 3 files changed, 68 insertions(+), 1 deletion(-)
>
>diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
>index 9d5b7b51bdfa..66e46528e3bd 100644
>--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
>+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
>@@ -1048,6 +1048,8 @@ int rvu_nix_mcast_get_mce_index(struct rvu *rvu, u16 pcifunc,
> u32 mcast_grp_idx);
> int rvu_nix_mcast_update_mcam_entry(struct rvu *rvu, u16 pcifunc,
> u32 mcast_grp_idx, u16 mcam_index);
>+int rvu_nix_tl1_xoff_wait_for_link_credits(struct rvu *rvu, u16 pcifunc);
>+int rvu_nix_tl1_xoff_clear(struct rvu *rvu, u16 pcifunc);
> void rvu_nix_flr_free_bpids(struct rvu *rvu, u16 pcifunc);
> int rvu_alloc_cint_qint_mem(struct rvu *rvu, struct rvu_pfvf *pfvf,
> int blkaddr, int nixlf);
>diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
>index 87d21889dc49..85143684bb6f 100644
>--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
>+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
>@@ -234,7 +234,7 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu)
> struct cgx_link_user_info *linfo;
> struct cgx_link_info_msg *msg;
> unsigned long pfmap;
>- int pfid;
>+ int pfid, err;
>
> linfo = &event->link_uinfo;
> pfmap = cgxlmac_to_pfmap(rvu, event->cgx_id, event->lmac_id);
>@@ -249,6 +249,15 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu)
> rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx);
> clear_bit(pfid, &pfmap);
>
>+ /* clear TL1 sw_xoff */
>+ if (linfo->link_up) {
>+ err = rvu_nix_tl1_xoff_clear(rvu, pfid << 10);
>+ if (err)
>+ dev_warn(rvu->dev,
>+ "tl1 sw_xoff clear unsuccessful, cgx=%d lmac=%d\n",
>+ event->cgx_id, event->lmac_id);
>+ }
>+
> /* check if notification is enabled */
> if (!test_bit(pfid, &rvu->pf_notify_bmap)) {
> dev_info(rvu->dev, "cgx %d: lmac %d Link status %s\n",
>@@ -1221,6 +1230,7 @@ int rvu_mbox_handler_cgx_set_link_mode(struct rvu *rvu,
> struct cgx_lmac_fwdata_s *linkmodes;
> u8 cgx_idx, lmac;
> void *cgxd;
>+ int err;
>
> if (!rvu->fwdata)
> return LMAC_AF_ERR_FIRMWARE_DATA_NOT_MAPPED;
>@@ -1230,11 +1240,18 @@ int rvu_mbox_handler_cgx_set_link_mode(struct rvu *rvu,
>
> rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_idx, &lmac);
> cgxd = rvu_cgx_pdata(cgx_idx, rvu);
>+
intentional or leftover?
> if (rvu->hw->lmac_per_cgx == CGX_LMACS_USX)
> linkmodes = &rvu->fwdata->cgx_fw_data_usx[cgx_idx][lmac];
> else
> linkmodes = &rvu->fwdata->cgx_fw_data[cgx_idx][lmac];
>
>+ err = rvu_nix_tl1_xoff_wait_for_link_credits(rvu, req->hdr.pcifunc);
>+ if (err)
>+ dev_warn(rvu->dev,
>+ "tl1 sw_xoff/link_credit_poll unsuccessful, cgx=%d lmac=%d\n",
>+ cgx_idx, lmac);
>+
> rsp->status = cgx_set_link_mode(cgxd, req->args, linkmodes,
> cgx_idx, lmac);
> return 0;
>diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
>index 964bcaae098e..92d8f318783d 100644
>--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
>+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
>@@ -6704,3 +6704,51 @@ void rvu_block_bcast_xon(struct rvu *rvu, int blkaddr)
> cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(0));
> rvu_write64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(0), cfg);
> }
>+
>+int rvu_nix_tl1_xoff_wait_for_link_credits(struct rvu *rvu, u16 pcifunc)
>+{
>+ int link, blkaddr, count = 1000;
count can be declared inside the loop as it's not used outside
same for regval
moreover defs instead of magic is always better choice
>+ u64 tx_credits, regval;
>+ struct nix_hw *nix_hw;
>+
>+ blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
>+ if (blkaddr < 0)
>+ return NIX_AF_ERR_AF_LF_INVALID;
>+
>+ nix_hw = get_nix_hw(rvu->hw, blkaddr);
>+ if (!nix_hw)
>+ return NIX_AF_ERR_INVALID_NIXBLK;
>+
>+ /* set TL1 sw_xoff */
>+ link = nix_get_tx_link(rvu, pcifunc);
>+ rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SW_XOFF(link), 1);
>+
>+ /* wait for link credits to return */
>+ tx_credits = nix_hw->tx_credits[link];
>+ while (1) {
cant we use just for loop if this is simple cnt decrementing?
>+ regval = rvu_read64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link));
>+ if (((regval >> 12) & 0xFFFFF) == tx_credits)
FIELD_GET(GENMASK(31, 12), regval) ?
>+ break;
>+ count--;
>+ if (!count) {
>+ dev_err(rvu->dev, "TX link(%d) credit poll timeout\n", link);
>+ return -ETIMEDOUT;
>+ }
>+ cpu_relax();
>+ udelay(1);
>+ }
>+ return 0;
>+}
>+
>+int rvu_nix_tl1_xoff_clear(struct rvu *rvu, u16 pcifunc)
>+{
>+ int blkaddr;
>+
>+ blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
>+ if (blkaddr < 0)
>+ return NIX_AF_ERR_AF_LF_INVALID;
>+
>+ nix_clear_tx_xoff(rvu, blkaddr, NIX_TXSCH_LVL_TL1,
>+ nix_get_tx_link(rvu, pcifunc));
>+ return 0;
>+}
>--
>2.43.0