Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit acb5aefd authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Greg Kroah-Hartman
Browse files

genirq: Avoid summation loops for /proc/stat



[ 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: default avatarWaiman Long <longman@redhat.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarWaiman Long <longman@redhat.com>
Reviewed-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
Reviewed-by: default avatarDavidlohr 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: default avatarSasha Levin <sashal@kernel.org>
parent e940258c
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -61,6 +61,7 @@ struct irq_desc {
	unsigned int		core_internal_state__do_not_mess_with_it;
	unsigned int		core_internal_state__do_not_mess_with_it;
	unsigned int		depth;		/* nested irq disables */
	unsigned int		depth;		/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		tot_count;
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	unsigned int		irqs_unhandled;
	unsigned int		irqs_unhandled;
+10 −2
Original line number Original line Diff line number Diff line
@@ -729,7 +729,11 @@ void handle_percpu_irq(struct irq_desc *desc)
{
{
	struct irq_chip *chip = irq_desc_get_chip(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)
	if (chip->irq_ack)
		chip->irq_ack(&desc->irq_data);
		chip->irq_ack(&desc->irq_data);
@@ -758,7 +762,11 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
	unsigned int irq = irq_desc_get_irq(desc);
	unsigned int irq = irq_desc_get_irq(desc);
	irqreturn_t res;
	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)
	if (chip->irq_ack)
		chip->irq_ack(&desc->irq_data);
		chip->irq_ack(&desc->irq_data);
+7 −1
Original line number Original line Diff line number Diff line
@@ -199,12 +199,18 @@ static inline bool irqd_has_set(struct irq_data *d, unsigned int mask)


#undef __irqd_to_state
#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(*desc->kstat_irqs);
	__this_cpu_inc(kstat.irqs_sum);
	__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)
static inline int irq_desc_get_node(struct irq_desc *desc)
{
{
	return irq_common_data_get_node(&desc->irq_common_data);
	return irq_common_data_get_node(&desc->irq_common_data);
+6 −1
Original line number Original line Diff line number Diff line
@@ -109,6 +109,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
	desc->depth = 1;
	desc->depth = 1;
	desc->irq_count = 0;
	desc->irq_count = 0;
	desc->irqs_unhandled = 0;
	desc->irqs_unhandled = 0;
	desc->tot_count = 0;
	desc->name = NULL;
	desc->name = NULL;
	desc->owner = owner;
	desc->owner = owner;
	for_each_possible_cpu(cpu)
	for_each_possible_cpu(cpu)
@@ -880,11 +881,15 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
unsigned int kstat_irqs(unsigned int irq)
unsigned int kstat_irqs(unsigned int irq)
{
{
	struct irq_desc *desc = irq_to_desc(irq);
	struct irq_desc *desc = irq_to_desc(irq);
	int cpu;
	unsigned int sum = 0;
	unsigned int sum = 0;
	int cpu;


	if (!desc || !desc->kstat_irqs)
	if (!desc || !desc->kstat_irqs)
		return 0;
		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)
	for_each_possible_cpu(cpu)
		sum += *per_cpu_ptr(desc->kstat_irqs, cpu);
		sum += *per_cpu_ptr(desc->kstat_irqs, cpu);
	return sum;
	return sum;