genirq: Robustify handle_percpu_devid_irq()
[cascardo/linux.git] / kernel / irq / chip.c
index 2f9f2b0..93c373a 100644 (file)
@@ -426,6 +426,49 @@ out_unlock:
 }
 EXPORT_SYMBOL_GPL(handle_simple_irq);
 
+/**
+ *     handle_untracked_irq - Simple and software-decoded IRQs.
+ *     @desc:  the interrupt description structure for this irq
+ *
+ *     Untracked interrupts are sent from a demultiplexing interrupt
+ *     handler when the demultiplexer does not know which device it its
+ *     multiplexed irq domain generated the interrupt. IRQ's handled
+ *     through here are not subjected to stats tracking, randomness, or
+ *     spurious interrupt detection.
+ *
+ *     Note: Like handle_simple_irq, the caller is expected to handle
+ *     the ack, clear, mask and unmask issues if necessary.
+ */
+void handle_untracked_irq(struct irq_desc *desc)
+{
+       unsigned int flags = 0;
+
+       raw_spin_lock(&desc->lock);
+
+       if (!irq_may_run(desc))
+               goto out_unlock;
+
+       desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
+
+       if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
+               desc->istate |= IRQS_PENDING;
+               goto out_unlock;
+       }
+
+       desc->istate &= ~IRQS_PENDING;
+       irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
+       raw_spin_unlock(&desc->lock);
+
+       __handle_irq_event_percpu(desc, &flags);
+
+       raw_spin_lock(&desc->lock);
+       irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
+
+out_unlock:
+       raw_spin_unlock(&desc->lock);
+}
+EXPORT_SYMBOL_GPL(handle_untracked_irq);
+
 /*
  * Called unconditionally from handle_level_irq() and only for oneshot
  * interrupts from handle_fasteoi_irq()
@@ -713,7 +756,6 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
        struct irqaction *action = desc->action;
-       void *dev_id = raw_cpu_ptr(action->percpu_dev_id);
        unsigned int irq = irq_desc_get_irq(desc);
        irqreturn_t res;
 
@@ -722,9 +764,20 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
        if (chip->irq_ack)
                chip->irq_ack(&desc->irq_data);
 
-       trace_irq_handler_entry(irq, action);
-       res = action->handler(irq, dev_id);
-       trace_irq_handler_exit(irq, action, res);
+       if (likely(action)) {
+               trace_irq_handler_entry(irq, action);
+               res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
+               trace_irq_handler_exit(irq, action, res);
+       } else {
+               unsigned int cpu = smp_processor_id();
+               bool enabled = cpumask_test_cpu(cpu, desc->percpu_enabled);
+
+               if (enabled)
+                       irq_percpu_disable(desc, cpu);
+
+               pr_err_once("Spurious%s percpu IRQ%u on CPU%u\n",
+                           enabled ? " and unmasked" : "", irq, cpu);
+       }
 
        if (chip->irq_eoi)
                chip->irq_eoi(&desc->irq_data);
@@ -1093,3 +1146,43 @@ int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
 
        return 0;
 }
+
+/**
+ * irq_chip_pm_get - Enable power for an IRQ chip
+ * @data:      Pointer to interrupt specific data
+ *
+ * Enable the power to the IRQ chip referenced by the interrupt data
+ * structure.
+ */
+int irq_chip_pm_get(struct irq_data *data)
+{
+       int retval;
+
+       if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device) {
+               retval = pm_runtime_get_sync(data->chip->parent_device);
+               if (retval < 0) {
+                       pm_runtime_put_noidle(data->chip->parent_device);
+                       return retval;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * irq_chip_pm_put - Disable power for an IRQ chip
+ * @data:      Pointer to interrupt specific data
+ *
+ * Disable the power to the IRQ chip referenced by the interrupt data
+ * structure, belongs. Note that power will only be disabled, once this
+ * function has been called for all IRQs that have called irq_chip_pm_get().
+ */
+int irq_chip_pm_put(struct irq_data *data)
+{
+       int retval = 0;
+
+       if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device)
+               retval = pm_runtime_put(data->chip->parent_device);
+
+       return (retval < 0) ? retval : 0;
+}