From: Michael Kelley <mhklinux@xxxxxxxxxxx> Hyper-V VMBus devices generate interrupts that are multiplexed onto a single per-CPU architectural interrupt. The top-level VMBus driver ISR demultiplexes these interrupts and invokes per-device handlers. Currently, these per-device handlers are not modeled as Linux IRQs, so /proc/interrupts shows all VMBus interrupts as accounted to the top level architectural interrupt. Visibility into per-device interrupt stats requires accessing VMBus-specific entries in sysfs. The top-level VMBus driver ISR also handles management-related interrupts that are not attributable to a particular VMBus device. As part of changing VMBus to model VMBus per-device handlers as normal Linux IRQs, the top-level VMBus driver needs to conditionally account for interrupts. If it passes the interrupt off to a device-specific IRQ, the interrupt stats are done by that IRQ handler, and accounting for the interrupt at the top level is duplicative. But if it handles a management-related interrupt itself, then it should account for the interrupt itself. Introduce a new flow handler that provides this functionality. The new handler parallels handle_percpu_irq(), but does stats only if the ISR returns other than IRQ_NONE. The existing handle_untracked_irq() can't be used because it doesn't work for per-cpu IRQs, and it doesn't provide conditional stats. Signed-off-by: Michael Kelley <mhklinux@xxxxxxxxxxx> --- include/linux/irq.h | 1 + kernel/irq/chip.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/linux/irq.h b/include/linux/irq.h index a217e1029c1d..8825b95cefe5 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -656,6 +656,7 @@ extern void handle_edge_eoi_irq(struct irq_desc *desc); extern void handle_simple_irq(struct irq_desc *desc); extern void handle_untracked_irq(struct irq_desc *desc); extern void handle_percpu_irq(struct irq_desc *desc); +extern void handle_percpu_demux_irq(struct irq_desc *desc); extern void handle_percpu_devid_irq(struct irq_desc *desc); extern void handle_bad_irq(struct irq_desc *desc); extern void handle_nested_irq(unsigned int irq); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index dc94e0bf2c94..1f37a9db4a4d 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -910,6 +910,35 @@ void handle_percpu_irq(struct irq_desc *desc) chip->irq_eoi(&desc->irq_data); } +/** + * handle_percpu_demux_irq - Per CPU local irq handler for IRQs + * that may demultiplex to other IRQs + * @desc: the interrupt description structure for this irq + * + * For per CPU interrupts on SMP machines without locking requirements. + * Used for IRQs that may demultiplex to other IRQs that will do the + * stats tracking and randomness. If the handler result indicates no + * demultiplexing is done, account for the interrupt on this IRQ. + */ +void handle_percpu_demux_irq(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + + if (chip->irq_ack) + chip->irq_ack(&desc->irq_data); + + if (__handle_irq_event_percpu(desc)) + /* + * PER CPU interrupts are not serialized. Do not touch + * desc->tot_count. + */ + __kstat_incr_irqs_this_cpu(desc); + + if (chip->irq_eoi) + chip->irq_eoi(&desc->irq_data); +} +EXPORT_SYMBOL_GPL(handle_percpu_demux_irq); + /** * handle_percpu_devid_irq - Per CPU local irq handler with per cpu dev ids * @desc: the interrupt description structure for this irq -- 2.25.1