]>
Commit | Line | Data |
---|---|---|
ba172962 SL |
1 | From a07167b5aa2226adf4d633069c055b9197f914fa Mon Sep 17 00:00:00 2001 |
2 | From: Thomas Gleixner <tglx@linutronix.de> | |
3 | Date: Fri, 8 Feb 2019 14:48:03 +0100 | |
4 | Subject: genirq: Avoid summation loops for /proc/stat | |
5 | ||
6 | [ Upstream commit 1136b0728969901a091f0471968b2b76ed14d9ad ] | |
7 | ||
8 | Waiman reported that on large systems with a large amount of interrupts the | |
9 | readout of /proc/stat takes a long time to sum up the interrupt | |
10 | statistics. In principle this is not a problem. but for unknown reasons | |
11 | some enterprise quality software reads /proc/stat with a high frequency. | |
12 | ||
13 | The reason for this is that interrupt statistics are accounted per cpu. So | |
14 | the /proc/stat logic has to sum up the interrupt stats for each interrupt. | |
15 | ||
16 | This can be largely avoided for interrupts which are not marked as | |
17 | 'PER_CPU' interrupts by simply adding a per interrupt summation counter | |
18 | which is incremented along with the per interrupt per cpu counter. | |
19 | ||
20 | The PER_CPU interrupts need to avoid that and use only per cpu accounting | |
21 | because they share the interrupt number and the interrupt descriptor and | |
22 | concurrent updates would conflict or require unwanted synchronization. | |
23 | ||
24 | Reported-by: Waiman Long <longman@redhat.com> | |
25 | Signed-off-by: Thomas Gleixner <tglx@linutronix.de> | |
26 | Reviewed-by: Waiman Long <longman@redhat.com> | |
27 | Reviewed-by: Marc Zyngier <marc.zyngier@arm.com> | |
28 | Reviewed-by: Davidlohr Bueso <dbueso@suse.de> | |
29 | Cc: Matthew Wilcox <willy@infradead.org> | |
30 | Cc: Andrew Morton <akpm@linux-foundation.org> | |
31 | Cc: Alexey Dobriyan <adobriyan@gmail.com> | |
32 | Cc: Kees Cook <keescook@chromium.org> | |
33 | Cc: linux-fsdevel@vger.kernel.org | |
34 | Cc: Davidlohr Bueso <dave@stgolabs.net> | |
35 | Cc: Miklos Szeredi <miklos@szeredi.hu> | |
36 | Cc: Daniel Colascione <dancol@google.com> | |
37 | Cc: Dave Chinner <david@fromorbit.com> | |
38 | Cc: Randy Dunlap <rdunlap@infradead.org> | |
39 | Link: https://lkml.kernel.org/r/20190208135020.925487496@linutronix.de | |
40 | ||
41 | 8<------------- | |
42 | ||
43 | v2: Undo the unintentional layout change of struct irq_desc. | |
44 | ||
45 | include/linux/irqdesc.h | 1 + | |
46 | kernel/irq/chip.c | 12 ++++++++++-- | |
47 | kernel/irq/internals.h | 8 +++++++- | |
48 | kernel/irq/irqdesc.c | 7 ++++++- | |
49 | 4 files changed, 24 insertions(+), 4 deletions(-) | |
50 | ||
51 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
52 | --- | |
53 | include/linux/irqdesc.h | 1 + | |
54 | kernel/irq/chip.c | 12 ++++++++++-- | |
55 | kernel/irq/internals.h | 8 +++++++- | |
56 | kernel/irq/irqdesc.c | 7 ++++++- | |
57 | 4 files changed, 24 insertions(+), 4 deletions(-) | |
58 | ||
59 | diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h | |
60 | index dd1e40ddac7d..875c41b23f20 100644 | |
61 | --- a/include/linux/irqdesc.h | |
62 | +++ b/include/linux/irqdesc.h | |
63 | @@ -65,6 +65,7 @@ struct irq_desc { | |
64 | unsigned int core_internal_state__do_not_mess_with_it; | |
65 | unsigned int depth; /* nested irq disables */ | |
66 | unsigned int wake_depth; /* nested wake enables */ | |
67 | + unsigned int tot_count; | |
68 | unsigned int irq_count; /* For detecting broken IRQs */ | |
69 | unsigned long last_unhandled; /* Aging timer for unhandled count */ | |
70 | unsigned int irqs_unhandled; | |
71 | diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c | |
72 | index a2b3d9de999c..811009ebacd4 100644 | |
73 | --- a/kernel/irq/chip.c | |
74 | +++ b/kernel/irq/chip.c | |
75 | @@ -855,7 +855,11 @@ void handle_percpu_irq(struct irq_desc *desc) | |
76 | { | |
77 | struct irq_chip *chip = irq_desc_get_chip(desc); | |
78 | ||
79 | - kstat_incr_irqs_this_cpu(desc); | |
80 | + /* | |
81 | + * PER CPU interrupts are not serialized. Do not touch | |
82 | + * desc->tot_count. | |
83 | + */ | |
84 | + __kstat_incr_irqs_this_cpu(desc); | |
85 | ||
86 | if (chip->irq_ack) | |
87 | chip->irq_ack(&desc->irq_data); | |
88 | @@ -884,7 +888,11 @@ void handle_percpu_devid_irq(struct irq_desc *desc) | |
89 | unsigned int irq = irq_desc_get_irq(desc); | |
90 | irqreturn_t res; | |
91 | ||
92 | - kstat_incr_irqs_this_cpu(desc); | |
93 | + /* | |
94 | + * PER CPU interrupts are not serialized. Do not touch | |
95 | + * desc->tot_count. | |
96 | + */ | |
97 | + __kstat_incr_irqs_this_cpu(desc); | |
98 | ||
99 | if (chip->irq_ack) | |
100 | chip->irq_ack(&desc->irq_data); | |
101 | diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h | |
102 | index ca6afa267070..e74e7eea76cf 100644 | |
103 | --- a/kernel/irq/internals.h | |
104 | +++ b/kernel/irq/internals.h | |
105 | @@ -242,12 +242,18 @@ static inline void irq_state_set_masked(struct irq_desc *desc) | |
106 | ||
107 | #undef __irqd_to_state | |
108 | ||
109 | -static inline void kstat_incr_irqs_this_cpu(struct irq_desc *desc) | |
110 | +static inline void __kstat_incr_irqs_this_cpu(struct irq_desc *desc) | |
111 | { | |
112 | __this_cpu_inc(*desc->kstat_irqs); | |
113 | __this_cpu_inc(kstat.irqs_sum); | |
114 | } | |
115 | ||
116 | +static inline void kstat_incr_irqs_this_cpu(struct irq_desc *desc) | |
117 | +{ | |
118 | + __kstat_incr_irqs_this_cpu(desc); | |
119 | + desc->tot_count++; | |
120 | +} | |
121 | + | |
122 | static inline int irq_desc_get_node(struct irq_desc *desc) | |
123 | { | |
124 | return irq_common_data_get_node(&desc->irq_common_data); | |
125 | diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c | |
126 | index 578d0e5f1b5b..ba454cba4069 100644 | |
127 | --- a/kernel/irq/irqdesc.c | |
128 | +++ b/kernel/irq/irqdesc.c | |
129 | @@ -119,6 +119,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node, | |
130 | desc->depth = 1; | |
131 | desc->irq_count = 0; | |
132 | desc->irqs_unhandled = 0; | |
133 | + desc->tot_count = 0; | |
134 | desc->name = NULL; | |
135 | desc->owner = owner; | |
136 | for_each_possible_cpu(cpu) | |
137 | @@ -915,11 +916,15 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) | |
138 | unsigned int kstat_irqs(unsigned int irq) | |
139 | { | |
140 | struct irq_desc *desc = irq_to_desc(irq); | |
141 | - int cpu; | |
142 | unsigned int sum = 0; | |
143 | + int cpu; | |
144 | ||
145 | if (!desc || !desc->kstat_irqs) | |
146 | return 0; | |
147 | + if (!irq_settings_is_per_cpu_devid(desc) && | |
148 | + !irq_settings_is_per_cpu(desc)) | |
149 | + return desc->tot_count; | |
150 | + | |
151 | for_each_possible_cpu(cpu) | |
152 | sum += *per_cpu_ptr(desc->kstat_irqs, cpu); | |
153 | return sum; | |
154 | -- | |
155 | 2.19.1 | |
156 |