diff options
Diffstat (limited to 'drivers/net/ethernet/cadence/macb_main.c')
-rw-r--r-- | drivers/net/ethernet/cadence/macb_main.c | 65 |
1 files changed, 31 insertions, 34 deletions
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 6434e74c04f1..160dc5ad84ae 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -1554,54 +1554,51 @@ static int macb_rx(struct macb_queue *queue, struct napi_struct *napi, return received; } +static bool macb_rx_pending(struct macb_queue *queue) +{ + struct macb *bp = queue->bp; + unsigned int entry; + struct macb_dma_desc *desc; + + entry = macb_rx_ring_wrap(bp, queue->rx_tail); + desc = macb_rx_desc(queue, entry); + + /* Make hw descriptor updates visible to CPU */ + rmb(); + + return (desc->addr & MACB_BIT(RX_USED)) != 0; +} + static int macb_poll(struct napi_struct *napi, int budget) { struct macb_queue *queue = container_of(napi, struct macb_queue, napi); struct macb *bp = queue->bp; int work_done; - u32 status; - status = macb_readl(bp, RSR); - macb_writel(bp, RSR, status); + work_done = bp->macbgem_ops.mog_rx(queue, napi, budget); - netdev_vdbg(bp->dev, "poll: status = %08lx, budget = %d\n", - (unsigned long)status, budget); + netdev_vdbg(bp->dev, "poll: queue = %u, work_done = %d, budget = %d\n", + (unsigned int)(queue - bp->queues), work_done, budget); - work_done = bp->macbgem_ops.mog_rx(queue, napi, budget); - if (work_done < budget) { - napi_complete_done(napi, work_done); + if (work_done < budget && napi_complete_done(napi, work_done)) { + queue_writel(queue, IER, bp->rx_intr_mask); - /* RSR bits only seem to propagate to raise interrupts when - * interrupts are enabled at the time, so if bits are already - * set due to packets received while interrupts were disabled, + /* Packet completions only seem to propagate to raise + * interrupts when interrupts are enabled at the time, so if + * packets were received while interrupts were disabled, * they will not cause another interrupt to be generated when * interrupts are re-enabled. - * Check for this case here. This has been seen to happen - * around 30% of the time under heavy network load. + * Check for this case here to avoid losing a wakeup. This can + * potentially race with the interrupt handler doing the same + * actions if an interrupt is raised just after enabling them, + * but this should be harmless. */ - status = macb_readl(bp, RSR); - if (status) { + if (macb_rx_pending(queue)) { + queue_writel(queue, IDR, bp->rx_intr_mask); if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) queue_writel(queue, ISR, MACB_BIT(RCOMP)); - napi_reschedule(napi); - } else { - queue_writel(queue, IER, bp->rx_intr_mask); - - /* In rare cases, packets could have been received in - * the window between the check above and re-enabling - * interrupts. Therefore, a double-check is required - * to avoid losing a wakeup. This can potentially race - * with the interrupt handler doing the same actions - * if an interrupt is raised just after enabling them, - * but this should be harmless. - */ - status = macb_readl(bp, RSR); - if (unlikely(status)) { - queue_writel(queue, IDR, bp->rx_intr_mask); - if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) - queue_writel(queue, ISR, MACB_BIT(RCOMP)); - napi_schedule(napi); - } + netdev_vdbg(bp->dev, "poll: packets pending, reschedule\n"); + napi_schedule(napi); } } |