]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
genirq: Avoid summation loops for /proc/stat
authorThomas Gleixner <tglx@linutronix.de>
Fri, 8 Feb 2019 13:48:03 +0000 (14:48 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 5 Apr 2019 20:31:35 +0000 (22:31 +0200)
[ Upstream commit 1136b0728969901a091f0471968b2b76ed14d9ad ]

Waiman reported that on large systems with a large amount of interrupts the
readout of /proc/stat takes a long time to sum up the interrupt
statistics. In principle this is not a problem. but for unknown reasons
some enterprise quality software reads /proc/stat with a high frequency.

The reason for this is that interrupt statistics are accounted per cpu. So
the /proc/stat logic has to sum up the interrupt stats for each interrupt.

This can be largely avoided for interrupts which are not marked as
'PER_CPU' interrupts by simply adding a per interrupt summation counter
which is incremented along with the per interrupt per cpu counter.

The PER_CPU interrupts need to avoid that and use only per cpu accounting
because they share the interrupt number and the interrupt descriptor and
concurrent updates would conflict or require unwanted synchronization.

Reported-by: Waiman Long <longman@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Waiman Long <longman@redhat.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Davidlohr Bueso <dbueso@suse.de>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: linux-fsdevel@vger.kernel.org
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Daniel Colascione <dancol@google.com>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Link: https://lkml.kernel.org/r/20190208135020.925487496@linutronix.de
8<-------------

v2: Undo the unintentional layout change of struct irq_desc.

 include/linux/irqdesc.h |    1 +
 kernel/irq/chip.c       |   12 ++++++++++--
 kernel/irq/internals.h  |    8 +++++++-
 kernel/irq/irqdesc.c    |    7 ++++++-
 4 files changed, 24 insertions(+), 4 deletions(-)

Signed-off-by: Sasha Levin <sashal@kernel.org>
include/linux/irqdesc.h
kernel/irq/chip.c
kernel/irq/internals.h
kernel/irq/irqdesc.c

index b6084898d33017e01edaef49893169a26c9c0164..234f0d1f8dcaf928a3a09e45c817b205c4212a6f 100644 (file)
@@ -65,6 +65,7 @@ struct irq_desc {
        unsigned int            core_internal_state__do_not_mess_with_it;
        unsigned int            depth;          /* nested irq disables */
        unsigned int            wake_depth;     /* nested wake enables */
+       unsigned int            tot_count;
        unsigned int            irq_count;      /* For detecting broken IRQs */
        unsigned long           last_unhandled; /* Aging timer for unhandled count */
        unsigned int            irqs_unhandled;
index 5a2ef92c2782c59c177e1f6c2eb5b4b17fb75a49..0fa7ef74303b1cb009ae26d3315243f7f6ce1600 100644 (file)
@@ -834,7 +834,11 @@ void handle_percpu_irq(struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
 
-       kstat_incr_irqs_this_cpu(desc);
+       /*
+        * PER CPU interrupts are not serialized. Do not touch
+        * desc->tot_count.
+        */
+       __kstat_incr_irqs_this_cpu(desc);
 
        if (chip->irq_ack)
                chip->irq_ack(&desc->irq_data);
@@ -863,7 +867,11 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
        unsigned int irq = irq_desc_get_irq(desc);
        irqreturn_t res;
 
-       kstat_incr_irqs_this_cpu(desc);
+       /*
+        * PER CPU interrupts are not serialized. Do not touch
+        * desc->tot_count.
+        */
+       __kstat_incr_irqs_this_cpu(desc);
 
        if (chip->irq_ack)
                chip->irq_ack(&desc->irq_data);
index 44ed5f8c8759051896fcdae2a38ae6eb0c4edd83..4ef7f3b820ce6aab4629fcf421bf53c6f0972de7 100644 (file)
@@ -240,12 +240,18 @@ static inline void irq_state_set_masked(struct irq_desc *desc)
 
 #undef __irqd_to_state
 
-static inline void kstat_incr_irqs_this_cpu(struct irq_desc *desc)
+static inline void __kstat_incr_irqs_this_cpu(struct irq_desc *desc)
 {
        __this_cpu_inc(*desc->kstat_irqs);
        __this_cpu_inc(kstat.irqs_sum);
 }
 
+static inline void kstat_incr_irqs_this_cpu(struct irq_desc *desc)
+{
+       __kstat_incr_irqs_this_cpu(desc);
+       desc->tot_count++;
+}
+
 static inline int irq_desc_get_node(struct irq_desc *desc)
 {
        return irq_common_data_get_node(&desc->irq_common_data);
index e97bbae947f0e178cfb605ecaae09c65b7a39012..c2bfb11a9d055ee175c892c995a29c82ae889a8f 100644 (file)
@@ -119,6 +119,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
        desc->depth = 1;
        desc->irq_count = 0;
        desc->irqs_unhandled = 0;
+       desc->tot_count = 0;
        desc->name = NULL;
        desc->owner = owner;
        for_each_possible_cpu(cpu)
@@ -895,11 +896,15 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
 unsigned int kstat_irqs(unsigned int irq)
 {
        struct irq_desc *desc = irq_to_desc(irq);
-       int cpu;
        unsigned int sum = 0;
+       int cpu;
 
        if (!desc || !desc->kstat_irqs)
                return 0;
+       if (!irq_settings_is_per_cpu_devid(desc) &&
+           !irq_settings_is_per_cpu(desc))
+           return desc->tot_count;
+
        for_each_possible_cpu(cpu)
                sum += *per_cpu_ptr(desc->kstat_irqs, cpu);
        return sum;