sfc: Work around unreliable legacy interrupt status

In rare cases, reading the legacy interrupt status register can
acknowledge an event queue whose attention flag has not yet been set
in the register.  Until we service this event queue it will not
generate any more interrupts.  Therefore, as a secondary check, poll
the next slot in each active event queue whose flag is not set.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 2ae51fd..92ea614 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -1435,6 +1435,7 @@
 {
 	struct efx_nic *efx = dev_id;
 	efx_oword_t *int_ker = efx->irq_status.addr;
+	irqreturn_t result = IRQ_NONE;
 	struct efx_channel *channel;
 	efx_dword_t reg;
 	u32 queues;
@@ -1449,23 +1450,24 @@
 	if (unlikely(syserr))
 		return falcon_fatal_interrupt(efx);
 
-	if (queues == 0)
-		return IRQ_NONE;
-
-	efx->last_irq_cpu = raw_smp_processor_id();
-	EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n",
-		  irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg));
-
 	/* Schedule processing of any interrupting queues */
-	channel = &efx->channel[0];
-	while (queues) {
-		if (queues & 0x01)
+	efx_for_each_channel(channel, efx) {
+		if ((queues & 1) ||
+		    falcon_event_present(
+			    falcon_event(channel, channel->eventq_read_ptr))) {
 			efx_schedule_channel(channel);
-		channel++;
+			result = IRQ_HANDLED;
+		}
 		queues >>= 1;
 	}
 
-	return IRQ_HANDLED;
+	if (result == IRQ_HANDLED) {
+		efx->last_irq_cpu = raw_smp_processor_id();
+		EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n",
+			  irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg));
+	}
+
+	return result;
 }