aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/irq/internals.h4
-rw-r--r--kernel/irq/irqdesc.c52
-rw-r--r--kernel/irq/proc.c22
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
80static inline void irq_mark_irq(unsigned int irq) { } 80static inline void irq_mark_irq(unsigned int irq) { }
81extern void irq_lock_sparse(void);
82extern void irq_unlock_sparse(void);
81#else 83#else
82extern void irq_mark_irq(unsigned int irq); 84extern void irq_mark_irq(unsigned int irq);
85static inline void irq_lock_sparse(void) { }
86static inline void irq_unlock_sparse(void) { }
83#endif 87#endif
84 88
85extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); 89extern 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)
132static inline void free_masks(struct irq_desc *desc) { } 132static inline void free_masks(struct irq_desc *desc) { }
133#endif 133#endif
134 134
135void irq_lock_sparse(void)
136{
137 mutex_lock(&sparse_irq_lock);
138}
139
140void irq_unlock_sparse(void)
141{
142 mutex_unlock(&sparse_irq_lock);
143}
144
135static struct irq_desc *alloc_desc(int irq, int node, struct module *owner) 145static 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 */
577unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) 602unsigned 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 */
585unsigned int kstat_irqs(unsigned int irq) 618unsigned 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 */
640unsigned 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 */
18static struct proc_dir_entry *root_irq_dir; 35static 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');
480out: 498out:
481 raw_spin_unlock_irqrestore(&desc->lock, flags); 499 raw_spin_unlock_irqrestore(&desc->lock, flags);
500outsparse:
501 irq_unlock_sparse();
482 return 0; 502 return 0;
483} 503}
484#endif 504#endif