Commit 82736f4d authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by Linus Torvalds
Browse files

generic irqs: handle failure of irqchip->set_type in setup_irq



set_type returns an int indicating success or failure, but up to now
setup_irq ignores that.

In my case this resulted in a machine hang:

gpio-keys requested IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, but
arm/ns9xxx can only trigger on one direction so set_type didn't touch
the configuration which happens do default to a level sensitiveness and
returned -EINVAL.  setup_irq ignored that and unmasked the irq.  This
resulted in an endless triggering of the gpio-key interrupt service
routine which effectively killed the machine.

With this patch applied setup_irq propagates the error to the caller.

Note that before in the case

	chip && !chip->set_type && !chip->name

a NULL pointer was feed to printk.  This is fixed, too.

Signed-off-by: default avatarUwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f606ddf4
Loading
Loading
Loading
Loading
+42 −22
Original line number Diff line number Diff line
@@ -308,6 +308,30 @@ void compat_irq_chip_set_default_handler(struct irq_desc *desc)
		desc->handle_irq = NULL;
}

static int __irq_set_trigger(struct irq_chip *chip, unsigned int irq,
		unsigned long flags)
{
	int ret;

	if (!chip || !chip->set_type) {
		/*
		 * IRQF_TRIGGER_* but the PIC does not support multiple
		 * flow-types?
		 */
		pr_warning("No set_type function for IRQ %d (%s)\n", irq,
				chip ? (chip->name ? : "unknown") : "unknown");
		return 0;
	}

	ret = chip->set_type(irq, flags & IRQF_TRIGGER_MASK);

	if (ret)
		pr_err("setting flow type for irq %u failed (%pF)\n",
				irq, chip->set_type);

	return ret;
}

/*
 * Internal function to register an irqaction - typically used to
 * allocate special interrupts that are part of the architecture.
@@ -319,6 +343,7 @@ int setup_irq(unsigned int irq, struct irqaction *new)
	const char *old_name = NULL;
	unsigned long flags;
	int shared = 0;
	int ret;

	if (irq >= NR_IRQS)
		return -EINVAL;
@@ -376,35 +401,23 @@ int setup_irq(unsigned int irq, struct irqaction *new)
		shared = 1;
	}

	*p = new;

	/* Exclude IRQ from balancing */
	if (new->flags & IRQF_NOBALANCING)
		desc->status |= IRQ_NO_BALANCING;

	if (!shared) {
		irq_chip_set_defaults(desc->chip);

#if defined(CONFIG_IRQ_PER_CPU)
		if (new->flags & IRQF_PERCPU)
			desc->status |= IRQ_PER_CPU;
#endif

		/* Setup the type (level, edge polarity) if configured: */
		if (new->flags & IRQF_TRIGGER_MASK) {
			if (desc->chip->set_type)
				desc->chip->set_type(irq,
						new->flags & IRQF_TRIGGER_MASK);
			else
				/*
				 * IRQF_TRIGGER_* but the PIC does not support
				 * multiple flow-types?
				 */
				printk(KERN_WARNING "No IRQF_TRIGGER set_type "
				       "function for IRQ %d (%s)\n", irq,
				       desc->chip->name);
			ret = __irq_set_trigger(desc->chip, irq, new->flags);

			if (ret) {
				spin_unlock_irqrestore(&desc->lock, flags);
				return ret;
			}
		} else
			compat_irq_chip_set_default_handler(desc);
#if defined(CONFIG_IRQ_PER_CPU)
		if (new->flags & IRQF_PERCPU)
			desc->status |= IRQ_PER_CPU;
#endif

		desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
				  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
@@ -423,6 +436,13 @@ int setup_irq(unsigned int irq, struct irqaction *new)
		/* Set default affinity mask once everything is setup */
		irq_select_affinity(irq);
	}

	*p = new;

	/* Exclude IRQ from balancing */
	if (new->flags & IRQF_NOBALANCING)
		desc->status |= IRQ_NO_BALANCING;

	/* Reset broken irq detection when installing new handler */
	desc->irq_count = 0;
	desc->irqs_unhandled = 0;