Commit f11a377b authored by David Dillow's avatar David Dillow Committed by David S. Miller

r8169: avoid losing MSI interrupts

The 8169 chip only generates MSI interrupts when all enabled event
sources are quiescent and one or more sources transition to active. If
not all of the active events are acknowledged, or a new event becomes
active while the existing ones are cleared in the handler, we will not
see a new interrupt.

The current interrupt handler masks off the Rx and Tx events once the
NAPI handler has been scheduled, which opens a race window in which we
can get another Rx or Tx event and never ACK'ing it, stopping all
activity until the link is reset (ifconfig down/up). Fix this by always
ACK'ing all event sources, and loop in the handler until we have all
sources quiescent.
Signed-off-by: default avatarDavid Dillow <dave@thedillows.org>
Tested-by: default avatarMichael Buesch <mb@bu3sch.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c80a5cdf
...@@ -3554,54 +3554,64 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) ...@@ -3554,54 +3554,64 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
int handled = 0; int handled = 0;
int status; int status;
/* loop handling interrupts until we have no new ones or
* we hit a invalid/hotplug case.
*/
status = RTL_R16(IntrStatus); status = RTL_R16(IntrStatus);
while (status && status != 0xffff) {
handled = 1;
/* hotplug/major error/no more work/shared irq */ /* Handle all of the error cases first. These will reset
if ((status == 0xffff) || !status) * the chip, so just exit the loop.
goto out; */
if (unlikely(!netif_running(dev))) {
handled = 1; rtl8169_asic_down(ioaddr);
break;
}
if (unlikely(!netif_running(dev))) { /* Work around for rx fifo overflow */
rtl8169_asic_down(ioaddr); if (unlikely(status & RxFIFOOver) &&
goto out; (tp->mac_version == RTL_GIGA_MAC_VER_11)) {
} netif_stop_queue(dev);
rtl8169_tx_timeout(dev);
break;
}
status &= tp->intr_mask; if (unlikely(status & SYSErr)) {
RTL_W16(IntrStatus, rtl8169_pcierr_interrupt(dev);
(status & RxFIFOOver) ? (status | RxOverflow) : status); break;
}
if (!(status & tp->intr_event)) if (status & LinkChg)
goto out; rtl8169_check_link_status(dev, tp, ioaddr);
/* Work around for rx fifo overflow */ /* We need to see the lastest version of tp->intr_mask to
if (unlikely(status & RxFIFOOver) && * avoid ignoring an MSI interrupt and having to wait for
(tp->mac_version == RTL_GIGA_MAC_VER_11)) { * another event which may never come.
netif_stop_queue(dev); */
rtl8169_tx_timeout(dev); smp_rmb();
goto out; if (status & tp->intr_mask & tp->napi_event) {
} RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
tp->intr_mask = ~tp->napi_event;
if (likely(napi_schedule_prep(&tp->napi)))
__napi_schedule(&tp->napi);
else if (netif_msg_intr(tp)) {
printk(KERN_INFO "%s: interrupt %04x in poll\n",
dev->name, status);
}
}
if (unlikely(status & SYSErr)) { /* We only get a new MSI interrupt when all active irq
rtl8169_pcierr_interrupt(dev); * sources on the chip have been acknowledged. So, ack
goto out; * everything we've seen and check if new sources have become
* active to avoid blocking all interrupts from the chip.
*/
RTL_W16(IntrStatus,
(status & RxFIFOOver) ? (status | RxOverflow) : status);
status = RTL_R16(IntrStatus);
} }
if (status & LinkChg)
rtl8169_check_link_status(dev, tp, ioaddr);
if (status & tp->napi_event) {
RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
tp->intr_mask = ~tp->napi_event;
if (likely(napi_schedule_prep(&tp->napi)))
__napi_schedule(&tp->napi);
else if (netif_msg_intr(tp)) {
printk(KERN_INFO "%s: interrupt %04x in poll\n",
dev->name, status);
}
}
out:
return IRQ_RETVAL(handled); return IRQ_RETVAL(handled);
} }
...@@ -3617,13 +3627,15 @@ static int rtl8169_poll(struct napi_struct *napi, int budget) ...@@ -3617,13 +3627,15 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
if (work_done < budget) { if (work_done < budget) {
napi_complete(napi); napi_complete(napi);
tp->intr_mask = 0xffff;
/* /* We need for force the visibility of tp->intr_mask
* 20040426: the barrier is not strictly required but the * for other CPUs, as we can loose an MSI interrupt
* behavior of the irq handler could be less predictable * and potentially wait for a retransmit timeout if we don't.
* without it. Btw, the lack of flush for the posted pci * The posted write to IntrMask is safe, as it will
* write is safe - FR * eventually make it to the chip and we won't loose anything
* until it does.
*/ */
tp->intr_mask = 0xffff;
smp_wmb(); smp_wmb();
RTL_W16(IntrMask, tp->intr_event); RTL_W16(IntrMask, tp->intr_event);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment