Hi,
When you get an unnecessary IRQ from the NIC; what was the cause of the previous (necessary) IRQ?
Note: There's multiple different "packet received" IRQs (e.g. "small receive packet detected", "receive descriptor minimum threshold reached", "receive timer interrupt") where it's possible to get 2 or more causes of an interrupt at once.I'm thinking something like:
1. The NIC receives a packet, sets the "packet received" bit in its ICR (interrupt cause register), and triggers an intterupt.
2. The interrupt handler is invoked and interrupts disabled (IF=0).
3a. The interrupt handler reads the ICR, which also automatically sets it to zero. At this point, therefore, the device should stop sending an interrupt.
3b. The NIC realises that at least one of the conditions that caused the IRQ is still present, and sets the bit in ICR again
3c. You send EOI to the PIC or IO APIC before you've finished handling the actual cause of the IRQ
3d. PIC or IO APIC tries to send the new/second IRQ to the CPU, but IRQs are disabled at the CPU so it gets postponed
4. The interrupt is handled.
4a. The ICR bit gets reset to zero because whatever condition/s that caused the IRQ are gone now
5. As soon as you enable interrupts, you get the interrupt that was postponed at "step 3d".
mariuszp wrote:
OK i guess i'll make the kernel go through the status register of every PCI device in this case, and if they're all clear, assume spurious and ignore.
There's no need to check drivers that aren't using that IRQ.
Note that the driver itself shouldn't know or care if the kernel felt like using PIC or IO APIC or MSI; and the driver itself also shouldn't know or care if the IRQ is being shared by other PCI devices or not. If the same IRQ actually was being shared by other PCI devices then your driver would broken (it would send an EOI even when the IRQ was intended for a different device driver that happens to be sharing the same IRQ). In other words, your driver should probably be considered broken simply because it assumes the IRQ is not shared.
In general; the kernel would have a list of none, one or more drivers that are using an IRQ. When that IRQ occurs the kernel would notify the drivers in the list one at a time. A driver would try to handle the IRQ and return some kind of "it was/wasn't my device" status back to the kernel. If the device returned "it was my device" then the kernel stops notifying any other drivers in that list and sends EOI. If the device returned "it was not my device" then kernel notifies the next driver in the list (if any).
If none of the drivers in the list say their device caused the IRQ, then the kernel assumes something is wrong. I'd be tempted to mask that IRQ and tell any/all drivers that they are being terminated. However, I'd also be tempted to use a "number of times in a row that this IRQ occurred for no reason" counter, which is reset to zero whenever any driver in the list says their device did cause the IRQ and incremented when none of them say their device caused the IRQ. If this counter reaches some threshold when it's incremented (maybe 3 unnecessary IRQs in a row?) then mask the IRQ and tell all the drivers they're being terminated. This gives some leniency while still guarding against IRQ floods.
Cheers,
Brendan