|
|
@@ -35,8 +35,9 @@ static int __init setup_forced_irqthreads(char *arg)
|
|
|
early_param("threadirqs", setup_forced_irqthreads);
|
|
|
#endif
|
|
|
|
|
|
-static void __synchronize_hardirq(struct irq_desc *desc)
|
|
|
+static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip)
|
|
|
{
|
|
|
+ struct irq_data *irqd = irq_desc_get_irq_data(desc);
|
|
|
bool inprogress;
|
|
|
|
|
|
do {
|
|
|
@@ -52,6 +53,20 @@ static void __synchronize_hardirq(struct irq_desc *desc)
|
|
|
/* Ok, that indicated we're done: double-check carefully. */
|
|
|
raw_spin_lock_irqsave(&desc->lock, flags);
|
|
|
inprogress = irqd_irq_inprogress(&desc->irq_data);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If requested and supported, check at the chip whether it
|
|
|
+ * is in flight at the hardware level, i.e. already pending
|
|
|
+ * in a CPU and waiting for service and acknowledge.
|
|
|
+ */
|
|
|
+ if (!inprogress && sync_chip) {
|
|
|
+ /*
|
|
|
+ * Ignore the return code. inprogress is only updated
|
|
|
+ * when the chip supports it.
|
|
|
+ */
|
|
|
+ __irq_get_irqchip_state(irqd, IRQCHIP_STATE_ACTIVE,
|
|
|
+ &inprogress);
|
|
|
+ }
|
|
|
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
|
|
|
|
|
/* Oops, that failed? */
|
|
|
@@ -74,13 +89,18 @@ static void __synchronize_hardirq(struct irq_desc *desc)
|
|
|
* Returns: false if a threaded handler is active.
|
|
|
*
|
|
|
* This function may be called - with care - from IRQ context.
|
|
|
+ *
|
|
|
+ * It does not check whether there is an interrupt in flight at the
|
|
|
+ * hardware level, but not serviced yet, as this might deadlock when
|
|
|
+ * called with interrupts disabled and the target CPU of the interrupt
|
|
|
+ * is the current CPU.
|
|
|
*/
|
|
|
bool synchronize_hardirq(unsigned int irq)
|
|
|
{
|
|
|
struct irq_desc *desc = irq_to_desc(irq);
|
|
|
|
|
|
if (desc) {
|
|
|
- __synchronize_hardirq(desc);
|
|
|
+ __synchronize_hardirq(desc, false);
|
|
|
return !atomic_read(&desc->threads_active);
|
|
|
}
|
|
|
|
|
|
@@ -98,13 +118,17 @@ EXPORT_SYMBOL(synchronize_hardirq);
|
|
|
*
|
|
|
* Can only be called from preemptible code as it might sleep when
|
|
|
* an interrupt thread is associated to @irq.
|
|
|
+ *
|
|
|
+ * It optionally makes sure (when the irq chip supports that method)
|
|
|
+ * that the interrupt is not pending in any CPU and waiting for
|
|
|
+ * service.
|
|
|
*/
|
|
|
void synchronize_irq(unsigned int irq)
|
|
|
{
|
|
|
struct irq_desc *desc = irq_to_desc(irq);
|
|
|
|
|
|
if (desc) {
|
|
|
- __synchronize_hardirq(desc);
|
|
|
+ __synchronize_hardirq(desc, true);
|
|
|
/*
|
|
|
* We made sure that no hardirq handler is
|
|
|
* running. Now verify that no threaded handlers are
|
|
|
@@ -1650,8 +1674,12 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
|
|
|
|
|
|
unregister_handler_proc(irq, action);
|
|
|
|
|
|
- /* Make sure it's not being used on another CPU: */
|
|
|
- synchronize_hardirq(irq);
|
|
|
+ /*
|
|
|
+ * Make sure it's not being used on another CPU and if the chip
|
|
|
+ * supports it also make sure that there is no (not yet serviced)
|
|
|
+ * interrupt in flight at the hardware level.
|
|
|
+ */
|
|
|
+ __synchronize_hardirq(desc, true);
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_SHIRQ
|
|
|
/*
|
|
|
@@ -2184,6 +2212,28 @@ int __request_percpu_irq(unsigned int irq, irq_handler_t handler,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(__request_percpu_irq);
|
|
|
|
|
|
+int __irq_get_irqchip_state(struct irq_data *data, enum irqchip_irq_state which,
|
|
|
+ bool *state)
|
|
|
+{
|
|
|
+ struct irq_chip *chip;
|
|
|
+ int err = -EINVAL;
|
|
|
+
|
|
|
+ do {
|
|
|
+ chip = irq_data_get_irq_chip(data);
|
|
|
+ if (chip->irq_get_irqchip_state)
|
|
|
+ break;
|
|
|
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
|
|
+ data = data->parent_data;
|
|
|
+#else
|
|
|
+ data = NULL;
|
|
|
+#endif
|
|
|
+ } while (data);
|
|
|
+
|
|
|
+ if (data)
|
|
|
+ err = chip->irq_get_irqchip_state(data, which, state);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* irq_get_irqchip_state - returns the irqchip state of a interrupt.
|
|
|
* @irq: Interrupt line that is forwarded to a VM
|
|
|
@@ -2202,7 +2252,6 @@ int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
|
|
|
{
|
|
|
struct irq_desc *desc;
|
|
|
struct irq_data *data;
|
|
|
- struct irq_chip *chip;
|
|
|
unsigned long flags;
|
|
|
int err = -EINVAL;
|
|
|
|
|
|
@@ -2212,19 +2261,7 @@ int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
|
|
|
|
|
|
data = irq_desc_get_irq_data(desc);
|
|
|
|
|
|
- do {
|
|
|
- chip = irq_data_get_irq_chip(data);
|
|
|
- if (chip->irq_get_irqchip_state)
|
|
|
- break;
|
|
|
-#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
|
|
- data = data->parent_data;
|
|
|
-#else
|
|
|
- data = NULL;
|
|
|
-#endif
|
|
|
- } while (data);
|
|
|
-
|
|
|
- if (data)
|
|
|
- err = chip->irq_get_irqchip_state(data, which, state);
|
|
|
+ err = __irq_get_irqchip_state(data, which, state);
|
|
|
|
|
|
irq_put_desc_busunlock(desc, flags);
|
|
|
return err;
|