summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Hao <haokexin@gmail.com>2026-04-02 16:41:24 +0300
committerJakub Kicinski <kuba@kernel.org>2026-04-04 01:51:04 +0300
commit6d55ce805b267400e682aa335787775bf253e5d8 (patch)
treeaf5f098e592d6c243fa16738545f35fbe73cc68f
parent5986ff6e4136105f16d481bd0532b7da2c6344ae (diff)
downloadlinux-6d55ce805b267400e682aa335787775bf253e5d8.tar.xz
net: macb: Factor out the handling of non-hot IRQ events into a separate function
In the current code, the IRQ handler checks each IRQ event sequentially. Since most IRQ events are related to TX/RX operations, while other events occur infrequently, this approach introduces unnecessary overhead in the hot path for TX/RX processing. This patch reduces such overhead by extracting the handling of all non-TX/RX events into a new function and consolidating these events under a new flag. As a result, only a single check is required to determine whether any non-TX/RX events have occurred. If such events exist, the handler jumps to the new function. This optimization reduces four conditional checks to one and prevents the instruction cache from being polluted with rarely used code in the hot path. Signed-off-by: Kevin Hao <haokexin@gmail.com> Link: https://patch.msgid.link/20260402-macb-irq-v2-3-942d98ab1154@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--drivers/net/ethernet/cadence/macb_main.c103
1 files changed, 61 insertions, 42 deletions
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index 248b82af8d3a..4fa75e6ed6d6 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -70,6 +70,9 @@ struct sifive_fu540_macb_mgmt {
#define MACB_TX_INT_FLAGS (MACB_TX_ERR_FLAGS | MACB_BIT(TCOMP) \
| MACB_BIT(TXUBR))
+#define MACB_INT_MISC_FLAGS (MACB_TX_ERR_FLAGS | MACB_BIT(RXUBR) | \
+ MACB_BIT(ISR_ROVR) | MACB_BIT(HRESP))
+
/* Max length of transmit frame must be a multiple of 8 bytes */
#define MACB_TX_LEN_ALIGN 8
#define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1)))
@@ -2078,12 +2081,66 @@ static irqreturn_t gem_wol_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int macb_interrupt_misc(struct macb_queue *queue, u32 status)
+{
+ struct macb *bp = queue->bp;
+ struct net_device *dev;
+ u32 ctrl;
+
+ dev = bp->dev;
+
+ if (unlikely(status & (MACB_TX_ERR_FLAGS))) {
+ queue_writel(queue, IDR, MACB_TX_INT_FLAGS);
+ schedule_work(&queue->tx_error_task);
+ macb_queue_isr_clear(bp, queue, MACB_TX_ERR_FLAGS);
+ return -1;
+ }
+
+ /* Link change detection isn't possible with RMII, so we'll
+ * add that if/when we get our hands on a full-blown MII PHY.
+ */
+
+ /* There is a hardware issue under heavy load where DMA can
+ * stop, this causes endless "used buffer descriptor read"
+ * interrupts but it can be cleared by re-enabling RX. See
+ * the at91rm9200 manual, section 41.3.1 or the Zynq manual
+ * section 16.7.4 for details. RXUBR is only enabled for
+ * these two versions.
+ */
+ if (status & MACB_BIT(RXUBR)) {
+ ctrl = macb_readl(bp, NCR);
+ macb_writel(bp, NCR, ctrl & ~MACB_BIT(RE));
+ wmb();
+ macb_writel(bp, NCR, ctrl | MACB_BIT(RE));
+ macb_queue_isr_clear(bp, queue, MACB_BIT(RXUBR));
+ }
+
+ if (status & MACB_BIT(ISR_ROVR)) {
+ /* We missed at least one packet */
+ spin_lock(&bp->stats_lock);
+ if (macb_is_gem(bp))
+ bp->hw_stats.gem.rx_overruns++;
+ else
+ bp->hw_stats.macb.rx_overruns++;
+ spin_unlock(&bp->stats_lock);
+ macb_queue_isr_clear(bp, queue, MACB_BIT(ISR_ROVR));
+ }
+
+ if (status & MACB_BIT(HRESP)) {
+ queue_work(system_bh_wq, &bp->hresp_err_bh_work);
+ netdev_err(dev, "DMA bus error: HRESP not OK\n");
+ macb_queue_isr_clear(bp, queue, MACB_BIT(HRESP));
+ }
+
+ return 0;
+}
+
static irqreturn_t macb_interrupt(int irq, void *dev_id)
{
struct macb_queue *queue = dev_id;
struct macb *bp = queue->bp;
struct net_device *dev = bp->dev;
- u32 status, ctrl;
+ u32 status;
status = queue_readl(queue, ISR);
@@ -2129,48 +2186,10 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
napi_schedule(&queue->napi_tx);
}
- if (unlikely(status & (MACB_TX_ERR_FLAGS))) {
- queue_writel(queue, IDR, MACB_TX_INT_FLAGS);
- schedule_work(&queue->tx_error_task);
- macb_queue_isr_clear(bp, queue, MACB_TX_ERR_FLAGS);
- break;
- }
-
- /* Link change detection isn't possible with RMII, so we'll
- * add that if/when we get our hands on a full-blown MII PHY.
- */
-
- /* There is a hardware issue under heavy load where DMA can
- * stop, this causes endless "used buffer descriptor read"
- * interrupts but it can be cleared by re-enabling RX. See
- * the at91rm9200 manual, section 41.3.1 or the Zynq manual
- * section 16.7.4 for details. RXUBR is only enabled for
- * these two versions.
- */
- if (status & MACB_BIT(RXUBR)) {
- ctrl = macb_readl(bp, NCR);
- macb_writel(bp, NCR, ctrl & ~MACB_BIT(RE));
- wmb();
- macb_writel(bp, NCR, ctrl | MACB_BIT(RE));
- macb_queue_isr_clear(bp, queue, MACB_BIT(RXUBR));
- }
-
- if (status & MACB_BIT(ISR_ROVR)) {
- /* We missed at least one packet */
- spin_lock(&bp->stats_lock);
- if (macb_is_gem(bp))
- bp->hw_stats.gem.rx_overruns++;
- else
- bp->hw_stats.macb.rx_overruns++;
- spin_unlock(&bp->stats_lock);
- macb_queue_isr_clear(bp, queue, MACB_BIT(ISR_ROVR));
- }
+ if (unlikely(status & MACB_INT_MISC_FLAGS))
+ if (macb_interrupt_misc(queue, status))
+ break;
- if (status & MACB_BIT(HRESP)) {
- queue_work(system_bh_wq, &bp->hresp_err_bh_work);
- netdev_err(dev, "DMA bus error: HRESP not OK\n");
- macb_queue_isr_clear(bp, queue, MACB_BIT(HRESP));
- }
status = queue_readl(queue, ISR);
}