diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/irq/internals.h | 4 | ||||
-rw-r--r-- | kernel/irq/irqdesc.c | 52 | ||||
-rw-r--r-- | kernel/irq/proc.c | 22 |
3 files changed, 77 insertions, 1 deletions
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 |