| From 95c2b17534654829db428f11bcf4297c059a2a7e Mon Sep 17 00:00:00 2001 |
| From: Ben Hutchings <ben@decadent.org.uk> |
| Date: Sat, 26 Sep 2015 12:23:56 +0100 |
| Subject: genirq: Fix race in register_irq_proc() |
| |
| From: Ben Hutchings <ben@decadent.org.uk> |
| |
| commit 95c2b17534654829db428f11bcf4297c059a2a7e upstream. |
| |
| Per-IRQ directories in procfs are created only when a handler is first |
| added to the irqdesc, not when the irqdesc is created. In the case of |
| a shared IRQ, multiple tasks can race to create a directory. This |
| race condition seems to have been present forever, but is easier to |
| hit with async probing. |
| |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| Link: http://lkml.kernel.org/r/1443266636.2004.2.camel@decadent.org.uk |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/irq/proc.c | 19 +++++++++++++++++-- |
| 1 file changed, 17 insertions(+), 2 deletions(-) |
| |
| --- a/kernel/irq/proc.c |
| +++ b/kernel/irq/proc.c |
| @@ -12,6 +12,7 @@ |
| #include <linux/seq_file.h> |
| #include <linux/interrupt.h> |
| #include <linux/kernel_stat.h> |
| +#include <linux/mutex.h> |
| |
| #include "internals.h" |
| |
| @@ -326,18 +327,29 @@ void register_handler_proc(unsigned int |
| |
| void register_irq_proc(unsigned int irq, struct irq_desc *desc) |
| { |
| + static DEFINE_MUTEX(register_lock); |
| char name [MAX_NAMELEN]; |
| |
| - if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip) || desc->dir) |
| + if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip)) |
| return; |
| |
| + /* |
| + * irq directories are registered only when a handler is |
| + * added, not when the descriptor is created, so multiple |
| + * tasks might try to register at the same time. |
| + */ |
| + mutex_lock(®ister_lock); |
| + |
| + if (desc->dir) |
| + goto out_unlock; |
| + |
| memset(name, 0, MAX_NAMELEN); |
| sprintf(name, "%d", irq); |
| |
| /* create /proc/irq/1234 */ |
| desc->dir = proc_mkdir(name, root_irq_dir); |
| if (!desc->dir) |
| - return; |
| + goto out_unlock; |
| |
| #ifdef CONFIG_SMP |
| /* create /proc/irq/<irq>/smp_affinity */ |
| @@ -358,6 +370,9 @@ void register_irq_proc(unsigned int irq, |
| |
| proc_create_data("spurious", 0444, desc->dir, |
| &irq_spurious_proc_fops, (void *)(long)irq); |
| + |
| +out_unlock: |
| + mutex_unlock(®ister_lock); |
| } |
| |
| void unregister_irq_proc(unsigned int irq, struct irq_desc *desc) |