From c3b744fd20191963f1739498446b5efb812c99d8 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Sat, 29 Nov 2025 23:20:15 +0530 Subject: [PATCH 1/2] amd-xgbe: refactor the dma IRQ handling code path Refactor the DMA interrupt bottom-half handling to improve the readability, maintainability, without changing the intended behavior. Signed-off-by: Raju Rangoju Link: https://patch.msgid.link/20251129175016.3034185-2-Raju.Rangoju@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 62 ++++++++++++++++-------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 0653e69f0ef7..aca1c57554d7 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -367,10 +367,11 @@ static irqreturn_t xgbe_ecc_isr(int irq, void *data) static void xgbe_isr_bh_work(struct work_struct *work) { struct xgbe_prv_data *pdata = from_work(pdata, work, dev_bh_work); - struct xgbe_hw_if *hw_if = &pdata->hw_if; - struct xgbe_channel *channel; - unsigned int dma_isr, dma_ch_isr; unsigned int mac_isr, mac_tssr, mac_mdioisr; + struct xgbe_hw_if *hw_if = &pdata->hw_if; + bool per_ch_irq, ti, ri, rbu, fbe; + unsigned int dma_isr, dma_ch_isr; + struct xgbe_channel *channel; unsigned int i; /* The DMA interrupt status register also reports MAC and MTL @@ -384,43 +385,62 @@ static void xgbe_isr_bh_work(struct work_struct *work) netif_dbg(pdata, intr, pdata->netdev, "DMA_ISR=%#010x\n", dma_isr); for (i = 0; i < pdata->channel_count; i++) { + bool schedule_napi = false; + struct napi_struct *napi; + if (!(dma_isr & (1 << i))) continue; channel = pdata->channel[i]; dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR); + + /* Precompute flags once */ + ti = !!XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI); + ri = !!XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI); + rbu = !!XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU); + fbe = !!XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE); + netif_dbg(pdata, intr, pdata->netdev, "DMA_CH%u_ISR=%#010x\n", i, dma_ch_isr); - /* The TI or RI interrupt bits may still be set even if using - * per channel DMA interrupts. Check to be sure those are not - * enabled before using the private data napi structure. - */ - if (!pdata->per_channel_irq && - (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI) || - XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI))) { - if (napi_schedule_prep(&pdata->napi)) { - /* Disable Tx and Rx interrupts */ - xgbe_disable_rx_tx_ints(pdata); + per_ch_irq = pdata->per_channel_irq; - /* Turn on polling */ - __napi_schedule(&pdata->napi); - } + /* + * Decide which NAPI to use and whether to schedule: + * - When not using per-channel IRQs: schedule on global NAPI + * if TI or RI are set. + */ + if (!per_ch_irq && (ti || ri)) + schedule_napi = true; + + napi = per_ch_irq ? &channel->napi : &pdata->napi; + + if (schedule_napi && napi_schedule_prep(napi)) { + /* Disable interrupts appropriately before polling */ + xgbe_disable_rx_tx_ints(pdata); + + /* Turn on polling */ + __napi_schedule(napi); } else { - /* Don't clear Rx/Tx status if doing per channel DMA - * interrupts, these will be cleared by the ISR for - * per channel DMA interrupts. + /* + * Don't clear Rx/Tx status if doing per-channel DMA + * interrupts; those bits will be serviced/cleared by + * the per-channel ISR/NAPI. In non-per-channel mode + * when we're not scheduling NAPI here, ensure we don't + * accidentally clear TI/RI in HW: zero them in the + * local copy so that the eventual write-back does not + * clear TI/RI. */ XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, TI, 0); XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, RI, 0); } - if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU)) + if (rbu) pdata->ext_stats.rx_buffer_unavailable++; /* Restart the device on a Fatal Bus Error */ - if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE)) + if (fbe) schedule_work(&pdata->restart_work); /* Clear interrupt signals */ From ab96af7004c76de5fdca077b1e3732a7ec96f69b Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Sat, 29 Nov 2025 23:20:16 +0530 Subject: [PATCH 2/2] amd-xgbe: schedule NAPI on Rx Buffer Unavailable (RBU) Under heavy load, Rx Buffer Unavailable (RBU) can occur if Rx processing is slower than network. When an RBU is signaled, try to schedule NAPI to help recover from such situation (including cases where an IRQ may be missed or such) Signed-off-by: Raju Rangoju Link: https://patch.msgid.link/20251129175016.3034185-3-Raju.Rangoju@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index aca1c57554d7..3ddd896d6987 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -410,15 +410,29 @@ static void xgbe_isr_bh_work(struct work_struct *work) * Decide which NAPI to use and whether to schedule: * - When not using per-channel IRQs: schedule on global NAPI * if TI or RI are set. + * - RBU should also trigger NAPI (either per-channel or global) + * to allow refill. */ if (!per_ch_irq && (ti || ri)) schedule_napi = true; + if (rbu) { + schedule_napi = true; + pdata->ext_stats.rx_buffer_unavailable++; + } + napi = per_ch_irq ? &channel->napi : &pdata->napi; if (schedule_napi && napi_schedule_prep(napi)) { /* Disable interrupts appropriately before polling */ - xgbe_disable_rx_tx_ints(pdata); + if (per_ch_irq) { + if (pdata->channel_irq_mode) + xgbe_disable_rx_tx_int(pdata, channel); + else + disable_irq_nosync(channel->dma_irq); + } else { + xgbe_disable_rx_tx_ints(pdata); + } /* Turn on polling */ __napi_schedule(napi); @@ -436,9 +450,6 @@ static void xgbe_isr_bh_work(struct work_struct *work) XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, RI, 0); } - if (rbu) - pdata->ext_stats.rx_buffer_unavailable++; - /* Restart the device on a Fatal Bus Error */ if (fbe) schedule_work(&pdata->restart_work);