diff options
author | Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com> | 2008-07-24 00:28:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:24 -0400 |
commit | 82736f4d1d2b7063b829cc93171a6e5aea8a9c49 (patch) | |
tree | 3e0e7bf57638dcb1321496440a11623beb005458 /kernel/irq/manage.c | |
parent | f606ddf42fd4edc558eeb48bfee66d2c591571d2 (diff) |
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: Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/irq/manage.c')
-rw-r--r-- | kernel/irq/manage.c | 64 |
1 files changed, 42 insertions, 22 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 3cfc0fefb5ee..5bc6e5ecc493 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -308,6 +308,30 @@ void compat_irq_chip_set_default_handler(struct irq_desc *desc) | |||
308 | desc->handle_irq = NULL; | 308 | desc->handle_irq = NULL; |
309 | } | 309 | } |
310 | 310 | ||
311 | static int __irq_set_trigger(struct irq_chip *chip, unsigned int irq, | ||
312 | unsigned long flags) | ||
313 | { | ||
314 | int ret; | ||
315 | |||
316 | if (!chip || !chip->set_type) { | ||
317 | /* | ||
318 | * IRQF_TRIGGER_* but the PIC does not support multiple | ||
319 | * flow-types? | ||
320 | */ | ||
321 | pr_warning("No set_type function for IRQ %d (%s)\n", irq, | ||
322 | chip ? (chip->name ? : "unknown") : "unknown"); | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | ret = chip->set_type(irq, flags & IRQF_TRIGGER_MASK); | ||
327 | |||
328 | if (ret) | ||
329 | pr_err("setting flow type for irq %u failed (%pF)\n", | ||
330 | irq, chip->set_type); | ||
331 | |||
332 | return ret; | ||
333 | } | ||
334 | |||
311 | /* | 335 | /* |
312 | * Internal function to register an irqaction - typically used to | 336 | * Internal function to register an irqaction - typically used to |
313 | * allocate special interrupts that are part of the architecture. | 337 | * allocate special interrupts that are part of the architecture. |
@@ -319,6 +343,7 @@ int setup_irq(unsigned int irq, struct irqaction *new) | |||
319 | const char *old_name = NULL; | 343 | const char *old_name = NULL; |
320 | unsigned long flags; | 344 | unsigned long flags; |
321 | int shared = 0; | 345 | int shared = 0; |
346 | int ret; | ||
322 | 347 | ||
323 | if (irq >= NR_IRQS) | 348 | if (irq >= NR_IRQS) |
324 | return -EINVAL; | 349 | return -EINVAL; |
@@ -376,35 +401,23 @@ int setup_irq(unsigned int irq, struct irqaction *new) | |||
376 | shared = 1; | 401 | shared = 1; |
377 | } | 402 | } |
378 | 403 | ||
379 | *p = new; | ||
380 | |||
381 | /* Exclude IRQ from balancing */ | ||
382 | if (new->flags & IRQF_NOBALANCING) | ||
383 | desc->status |= IRQ_NO_BALANCING; | ||
384 | |||
385 | if (!shared) { | 404 | if (!shared) { |
386 | irq_chip_set_defaults(desc->chip); | 405 | irq_chip_set_defaults(desc->chip); |
387 | 406 | ||
388 | #if defined(CONFIG_IRQ_PER_CPU) | ||
389 | if (new->flags & IRQF_PERCPU) | ||
390 | desc->status |= IRQ_PER_CPU; | ||
391 | #endif | ||
392 | |||
393 | /* Setup the type (level, edge polarity) if configured: */ | 407 | /* Setup the type (level, edge polarity) if configured: */ |
394 | if (new->flags & IRQF_TRIGGER_MASK) { | 408 | if (new->flags & IRQF_TRIGGER_MASK) { |
395 | if (desc->chip->set_type) | 409 | ret = __irq_set_trigger(desc->chip, irq, new->flags); |
396 | desc->chip->set_type(irq, | 410 | |
397 | new->flags & IRQF_TRIGGER_MASK); | 411 | if (ret) { |
398 | else | 412 | spin_unlock_irqrestore(&desc->lock, flags); |
399 | /* | 413 | return ret; |
400 | * IRQF_TRIGGER_* but the PIC does not support | 414 | } |
401 | * multiple flow-types? | ||
402 | */ | ||
403 | printk(KERN_WARNING "No IRQF_TRIGGER set_type " | ||
404 | "function for IRQ %d (%s)\n", irq, | ||
405 | desc->chip->name); | ||
406 | } else | 415 | } else |
407 | compat_irq_chip_set_default_handler(desc); | 416 | compat_irq_chip_set_default_handler(desc); |
417 | #if defined(CONFIG_IRQ_PER_CPU) | ||
418 | if (new->flags & IRQF_PERCPU) | ||
419 | desc->status |= IRQ_PER_CPU; | ||
420 | #endif | ||
408 | 421 | ||
409 | desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | | 422 | desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | |
410 | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); | 423 | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); |
@@ -423,6 +436,13 @@ int setup_irq(unsigned int irq, struct irqaction *new) | |||
423 | /* Set default affinity mask once everything is setup */ | 436 | /* Set default affinity mask once everything is setup */ |
424 | irq_select_affinity(irq); | 437 | irq_select_affinity(irq); |
425 | } | 438 | } |
439 | |||
440 | *p = new; | ||
441 | |||
442 | /* Exclude IRQ from balancing */ | ||
443 | if (new->flags & IRQF_NOBALANCING) | ||
444 | desc->status |= IRQ_NO_BALANCING; | ||
445 | |||
426 | /* Reset broken irq detection when installing new handler */ | 446 | /* Reset broken irq detection when installing new handler */ |
427 | desc->irq_count = 0; | 447 | desc->irq_count = 0; |
428 | desc->irqs_unhandled = 0; | 448 | desc->irqs_unhandled = 0; |