diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-19 16:26:08 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-19 16:26:08 -0500 |
| commit | ac88ee3b6cbac80c32556a39fe16c4bbc55fcbc5 (patch) | |
| tree | 5053b061053b244e1ed3fd55bc0736c8dd1e6753 | |
| parent | a54455766b9e3d3c27a6cef758355d2591d81d68 (diff) | |
| parent | c291ee622165cb2c8d4e7af63fffd499354a23be (diff) | |
Merge branch 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq core fix from Thomas Gleixner:
"A single fix plugging a long standing race between proc/stat and
proc/interrupts access and freeing of interrupt descriptors"
* 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
genirq: Prevent proc race against freeing of irq descriptors
| -rw-r--r-- | fs/proc/stat.c | 2 | ||||
| -rw-r--r-- | include/linux/kernel_stat.h | 1 | ||||
| -rw-r--r-- | kernel/irq/internals.h | 4 | ||||
| -rw-r--r-- | kernel/irq/irqdesc.c | 52 | ||||
| -rw-r--r-- | kernel/irq/proc.c | 22 |
5 files changed, 79 insertions, 2 deletions
diff --git a/fs/proc/stat.c b/fs/proc/stat.c index bf2d03f8fd3e..510413eb25b8 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c | |||
| @@ -159,7 +159,7 @@ static int show_stat(struct seq_file *p, void *v) | |||
| 159 | 159 | ||
| 160 | /* sum again ? it could be updated? */ | 160 | /* sum again ? it could be updated? */ |
| 161 | for_each_irq_nr(j) | 161 | for_each_irq_nr(j) |
| 162 | seq_put_decimal_ull(p, ' ', kstat_irqs(j)); | 162 | seq_put_decimal_ull(p, ' ', kstat_irqs_usr(j)); |
| 163 | 163 | ||
| 164 | seq_printf(p, | 164 | seq_printf(p, |
| 165 | "\nctxt %llu\n" | 165 | "\nctxt %llu\n" |
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index b9376cd5a187..25a822f6f000 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h | |||
| @@ -68,6 +68,7 @@ static inline unsigned int kstat_softirqs_cpu(unsigned int irq, int cpu) | |||
| 68 | * Number of interrupts per specific IRQ source, since bootup | 68 | * Number of interrupts per specific IRQ source, since bootup |
| 69 | */ | 69 | */ |
| 70 | extern unsigned int kstat_irqs(unsigned int irq); | 70 | extern unsigned int kstat_irqs(unsigned int irq); |
| 71 | extern unsigned int kstat_irqs_usr(unsigned int irq); | ||
| 71 | 72 | ||
| 72 | /* | 73 | /* |
| 73 | * Number of interrupts per cpu, since bootup | 74 | * Number of interrupts per cpu, since bootup |
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 4332d766619d..df553b0af936 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h | |||
| @@ -78,8 +78,12 @@ extern void unmask_threaded_irq(struct irq_desc *desc); | |||
| 78 | 78 | ||
| 79 | #ifdef CONFIG_SPARSE_IRQ | 79 | #ifdef CONFIG_SPARSE_IRQ |
| 80 | static inline void irq_mark_irq(unsigned int irq) { } | 80 | static inline void irq_mark_irq(unsigned int irq) { } |
| 81 | extern void irq_lock_sparse(void); | ||
| 82 | extern void irq_unlock_sparse(void); | ||
| 81 | #else | 83 | #else |
| 82 | extern void irq_mark_irq(unsigned int irq); | 84 | extern void irq_mark_irq(unsigned int irq); |
| 85 | static inline void irq_lock_sparse(void) { } | ||
| 86 | static inline void irq_unlock_sparse(void) { } | ||
| 83 | #endif | 87 | #endif |
| 84 | 88 | ||
| 85 | extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); | 89 | extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); |
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index a1782f88f0af..99793b9b6d23 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c | |||
| @@ -132,6 +132,16 @@ static void free_masks(struct irq_desc *desc) | |||
| 132 | static inline void free_masks(struct irq_desc *desc) { } | 132 | static inline void free_masks(struct irq_desc *desc) { } |
| 133 | #endif | 133 | #endif |
| 134 | 134 | ||
| 135 | void irq_lock_sparse(void) | ||
| 136 | { | ||
| 137 | mutex_lock(&sparse_irq_lock); | ||
| 138 | } | ||
| 139 | |||
| 140 | void irq_unlock_sparse(void) | ||
| 141 | { | ||
| 142 | mutex_unlock(&sparse_irq_lock); | ||
| 143 | } | ||
| 144 | |||
| 135 | static struct irq_desc *alloc_desc(int irq, int node, struct module *owner) | 145 | static struct irq_desc *alloc_desc(int irq, int node, struct module *owner) |
| 136 | { | 146 | { |
| 137 | struct irq_desc *desc; | 147 | struct irq_desc *desc; |
| @@ -168,6 +178,12 @@ static void free_desc(unsigned int irq) | |||
| 168 | 178 | ||
| 169 | unregister_irq_proc(irq, desc); | 179 | unregister_irq_proc(irq, desc); |
| 170 | 180 | ||
| 181 | /* | ||
| 182 | * sparse_irq_lock protects also show_interrupts() and | ||
| 183 | * kstat_irq_usr(). Once we deleted the descriptor from the | ||
| 184 | * sparse tree we can free it. Access in proc will fail to | ||
| 185 | * lookup the descriptor. | ||
| 186 | */ | ||
| 171 | mutex_lock(&sparse_irq_lock); | 187 | mutex_lock(&sparse_irq_lock); |
| 172 | delete_irq_desc(irq); | 188 | delete_irq_desc(irq); |
| 173 | mutex_unlock(&sparse_irq_lock); | 189 | mutex_unlock(&sparse_irq_lock); |
| @@ -574,6 +590,15 @@ void kstat_incr_irq_this_cpu(unsigned int irq) | |||
| 574 | kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); | 590 | kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); |
| 575 | } | 591 | } |
| 576 | 592 | ||
| 593 | /** | ||
| 594 | * kstat_irqs_cpu - Get the statistics for an interrupt on a cpu | ||
| 595 | * @irq: The interrupt number | ||
| 596 | * @cpu: The cpu number | ||
| 597 | * | ||
| 598 | * Returns the sum of interrupt counts on @cpu since boot for | ||
| 599 | * @irq. The caller must ensure that the interrupt is not removed | ||
| 600 | * concurrently. | ||
| 601 | */ | ||
| 577 | unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) | 602 | unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) |
| 578 | { | 603 | { |
| 579 | struct irq_desc *desc = irq_to_desc(irq); | 604 | struct irq_desc *desc = irq_to_desc(irq); |
| @@ -582,6 +607,14 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) | |||
| 582 | *per_cpu_ptr(desc->kstat_irqs, cpu) : 0; | 607 | *per_cpu_ptr(desc->kstat_irqs, cpu) : 0; |
| 583 | } | 608 | } |
| 584 | 609 | ||
| 610 | /** | ||
| 611 | * kstat_irqs - Get the statistics for an interrupt | ||
| 612 | * @irq: The interrupt number | ||
| 613 | * | ||
| 614 | * Returns the sum of interrupt counts on all cpus since boot for | ||
| 615 | * @irq. The caller must ensure that the interrupt is not removed | ||
| 616 | * concurrently. | ||
| 617 | */ | ||
| 585 | unsigned int kstat_irqs(unsigned int irq) | 618 | unsigned int kstat_irqs(unsigned int irq) |
| 586 | { | 619 | { |
| 587 | struct irq_desc *desc = irq_to_desc(irq); | 620 | struct irq_desc *desc = irq_to_desc(irq); |
| @@ -594,3 +627,22 @@ unsigned int kstat_irqs(unsigned int irq) | |||
| 594 | sum += *per_cpu_ptr(desc->kstat_irqs, cpu); | 627 | sum += *per_cpu_ptr(desc->kstat_irqs, cpu); |
| 595 | return sum; | 628 | return sum; |
| 596 | } | 629 | } |
| 630 | |||
| 631 | /** | ||
| 632 | * kstat_irqs_usr - Get the statistics for an interrupt | ||
| 633 | * @irq: The interrupt number | ||
| 634 | * | ||
| 635 | * Returns the sum of interrupt counts on all cpus since boot for | ||
| 636 | * @irq. Contrary to kstat_irqs() this can be called from any | ||
| 637 | * preemptible context. It's protected against concurrent removal of | ||
| 638 | * an interrupt descriptor when sparse irqs are enabled. | ||
| 639 | */ | ||
| 640 | unsigned int kstat_irqs_usr(unsigned int irq) | ||
| 641 | { | ||
| 642 | int sum; | ||
| 643 | |||
| 644 | irq_lock_sparse(); | ||
| 645 | sum = kstat_irqs(irq); | ||
| 646 | irq_unlock_sparse(); | ||
| 647 | return sum; | ||
| 648 | } | ||
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index ac1ba2f11032..9dc9bfd8a678 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c | |||
| @@ -15,6 +15,23 @@ | |||
| 15 | 15 | ||
| 16 | #include "internals.h" | 16 | #include "internals.h" |
| 17 | 17 | ||
| 18 | /* | ||
| 19 | * Access rules: | ||
| 20 | * | ||
| 21 | * procfs protects read/write of /proc/irq/N/ files against a | ||
| 22 | * concurrent free of the interrupt descriptor. remove_proc_entry() | ||
| 23 | * immediately prevents new read/writes to happen and waits for | ||
| 24 | * already running read/write functions to complete. | ||
| 25 | * | ||
| 26 | * We remove the proc entries first and then delete the interrupt | ||
| 27 | * descriptor from the radix tree and free it. So it is guaranteed | ||
| 28 | * that irq_to_desc(N) is valid as long as the read/writes are | ||
| 29 | * permitted by procfs. | ||
| 30 | * | ||
| 31 | * The read from /proc/interrupts is a different problem because there | ||
| 32 | * is no protection. So the lookup and the access to irqdesc | ||
| 33 | * information must be protected by sparse_irq_lock. | ||
| 34 | */ | ||
| 18 | static struct proc_dir_entry *root_irq_dir; | 35 | static struct proc_dir_entry *root_irq_dir; |
| 19 | 36 | ||
| 20 | #ifdef CONFIG_SMP | 37 | #ifdef CONFIG_SMP |
| @@ -437,9 +454,10 @@ int show_interrupts(struct seq_file *p, void *v) | |||
| 437 | seq_putc(p, '\n'); | 454 | seq_putc(p, '\n'); |
| 438 | } | 455 | } |
| 439 | 456 | ||
| 457 | irq_lock_sparse(); | ||
| 440 | desc = irq_to_desc(i); | 458 | desc = irq_to_desc(i); |
| 441 | if (!desc) | 459 | if (!desc) |
| 442 | return 0; | 460 | goto outsparse; |
| 443 | 461 | ||
| 444 | raw_spin_lock_irqsave(&desc->lock, flags); | 462 | raw_spin_lock_irqsave(&desc->lock, flags); |
| 445 | for_each_online_cpu(j) | 463 | for_each_online_cpu(j) |
| @@ -479,6 +497,8 @@ int show_interrupts(struct seq_file *p, void *v) | |||
| 479 | seq_putc(p, '\n'); | 497 | seq_putc(p, '\n'); |
| 480 | out: | 498 | out: |
| 481 | raw_spin_unlock_irqrestore(&desc->lock, flags); | 499 | raw_spin_unlock_irqrestore(&desc->lock, flags); |
| 500 | outsparse: | ||
| 501 | irq_unlock_sparse(); | ||
| 482 | return 0; | 502 | return 0; |
| 483 | } | 503 | } |
| 484 | #endif | 504 | #endif |
