genirq, proc: Speedup /proc/stat interrupt statistics
Subsystems (3)
cover genirq, proc: Speedup /proc/stat interrupt statistics
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.
The following series addresses this by making the interrupt statitics code in the core generate the sum directly and by making the loop in the /proc/stat read function smarter.
Thanks,
tglx
8<----------------
fs/proc/stat.c | 28 +++++++++++++++++++++++++--- include/linux/irqdesc.h | 3 ++- kernel/irq/chip.c | 12 ++++++++++-- kernel/irq/internals.h | 8 +++++++- kernel/irq/irqdesc.c | 7 ++++++- 5 files changed, 50 insertions(+), 8 deletions(-)
PATCH 1/2 genriq: Avoid summation loops for /proc/stat
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.
8<-------------
include/linux/irqdesc.h | 3 ++- kernel/irq/chip.c | 12 ++++++++++-- kernel/irq/internals.h | 8 +++++++- kernel/irq/irqdesc.c | 7 ++++++- 4 files changed, 25 insertions(+), 5 deletions(-)
include/linux/irqdesc.h +2 -1
@@ -65,9 +65,10 @@ 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; +unsigned long last_unhandled;/* Aging timer for unhandled count */ atomic_t threads_handled;int threads_handled_last;raw_spinlock_t lock;
kernel/irq/chip.c +10 -2
@@ -855,7 +855,11 @@ void handle_percpu_irq(struct irq_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);
@@ -884,7 +888,11 @@ void handle_percpu_devid_irq(struct irq_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);
kernel/irq/internals.h +7 -1
@@ -242,12 +242,18 @@ static inline void irq_state_set_masked(#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);
kernel/irq/irqdesc.c +6 -1
@@ -119,6 +119,7 @@ static void desc_set_defaults(unsigned i 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)
@@ -919,11 +920,15 @@ unsigned int kstat_irqs_cpu(unsigned intunsigned 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;
Re: [patch 1/2] genriq: Avoid summation loops for /proc/stat
Quoted 32 lines · Thomas Gleixner
Waiman reported that on large systems with a large amount of interrupts the readout of
/proc/stattakes 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/statwith a high frequency.The reason for this is that interrupt statistics are accounted per cpu. So the
/proc/statlogic 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_CPUinterrupts 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 <[email protected]>Signed-off-by Thomas Gleixner <[email protected]>8<-------------
include/linux/irqdesc.h | 3 ++- kernel/irq/chip.c | 12 ++++++++++-- kernel/irq/internals.h | 8 +++++++- kernel/irq/irqdesc.c | 7 ++++++- 4 files changed, 25 insertions(+), 5 deletions(-)include/linux/irqdesc.h +0
-0@@ -65,9 +65,10 @@ struct irq_desc {unsigned int core_internal_state__do_not_mess_with_it;
include/linux/irqdesc.h struct irq_desc { · line 74
Quoted 10 lines
include/linux/irqdesc.h +2
-1unsigned 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; +unsigned long last_unhandled;/* Aging timer for unhandled count */ atomic_t threads_handled;int threads_handled_last;raw_spinlock_t lock;
Just one minor nit. Why you want to move the last_unhandled down one slot? There were 5 int's before. Adding one more will just fill the padding hole. Moving down the last_unhandled will probably leave 4-byte holes in both above and below it assuming that raw_spinlock_t is 4 bytes.
Cheers,
Longman
Re: [patch 1/2] genriq: Avoid summation loops for /proc/stat
Quoted 12 lines · Waiman Long
Quoted 11 lines · Thomas Gleixner
include/linux/irqdesc.h +2
-1@@ -65,9 +65,10 @@ 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; +unsigned long last_unhandled;/* Aging timer for unhandled count */
Quoted 8 lines
include/linux/irqdesc.h struct irq_desc { · line 74
Quoted 3 lines
include/linux/irqdesc.h +0
-0atomic_t threads_handled;int threads_handled_last;raw_spinlock_t lock;Just one minor nit. Why you want to move the
last_unhandleddown one slot? There were 5 int's before. Adding one more will just fill the padding hole. Moving down thelast_unhandledwill probably leave 4-byte holes in both above and below it assuming thatraw_spinlock_tis 4 bytes.
Unintentional wreckage. Will undo. Thanks for spotting it.
Thanks,
tglx
PATCH 2/2 proc/stat: Make the interrupt statistics more efficient
In reply to Thomas GleixnerWaiman 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.
The interrupt core provides now a per interrupt summary counter which can be used to avoid the summation loops completely except for interrupts marked PER_CPU which are only a small fraction of the interrupt space if at all.
Another simplification is to iterate only over the active interrupts and skip the potentially large gaps in the interrupt number space and just print zeros for the gaps without going into the interrupt core in the first place.
--- fs/proc/stat.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-)
fs/proc/stat.c +25 -3
@@ -79,6 +79,30 @@ static u64 get_iowait_time(int cpu)#endif +static void show_irq_gap (struct seq_file *p,int gap) +{ +static const char zeros[] =" 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" ; + +while (gap >0 ) { +int inc =min_t (int, gap,ARRAY_SIZE (zeros) /2 ); + +seq_write (p, zeros,2 * inc); + gap -= inc; + } +} + +static void show_all_irqs (struct seq_file *p) +{ +int i, next =0 ; + +for_each_active_irq (i) { +show_irq_gap (p, i - next); +seq_put_decimal_ull (p," " ,kstat_irqs_usr (i)); + next = i +1 ; + } +show_irq_gap (p, nr_irqs - next); +} +static int show_stat (struct seq_file *p,void *v) {int i, j;
@@ -156,9 +180,7 @@ static int show_stat(struct seq_file *p, }seq_put_decimal_ull (p,"intr " , (unsigned long long )sum); -/* sum again ? it could be updated? */ -for_each_irq_nr (j) -seq_put_decimal_ull (p," " ,kstat_irqs_usr (j)); +show_all_irqs (p);seq_printf (p, "\nctxt %llu\n"
Re: [patch 2/2] proc/stat: Make the interrupt statistics more efficient
Quoted 13 lines · Thomas Gleixner
fs/proc/stat.c +13
-0+static void show_irq_gap (struct seq_file *p,int gap) +{ +static const char zeros[] =" 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" ; + +while (gap >0 ) { +int inc =min_t (int, gap,ARRAY_SIZE (zeros) /2 ); + +seq_write (p, zeros,2 * inc); + gap -= inc; + } +} + +static void show_all_irqs (struct seq_file *p)
fs/proc/stat.c static u64 get_iowait_time(int cpu) · line 104
Quoted 10 lines
fs/proc/stat.c +10
-0+{ +int i, next =0 ; + +for_each_active_irq (i) { +show_irq_gap (p, i - next); +seq_put_decimal_ull (p," " ,kstat_irqs_usr (i)); + next = i +1 ; + } +show_irq_gap (p, nr_irqs - next); +}
Every signed int can and should be unsigned int in this patch.
Re: [patch 2/2] proc/stat: Make the interrupt statistics more efficient
Quoted 18 lines · Alexey Dobriyan
Quoted 17 lines · Thomas Gleixner
fs/proc/stat.c +17
-0+static void show_irq_gap (struct seq_file *p,int gap) +{ +static const char zeros[] =" 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" ; + +while (gap >0 ) { +int inc =min_t (int, gap,ARRAY_SIZE (zeros) /2 ); + +seq_write (p, zeros,2 * inc); + gap -= inc; + } +} + +static void show_all_irqs (struct seq_file *p) +{ +int i, next =0 ; + +for_each_active_irq (i) {
Quoted 9 lines
fs/proc/stat.c static u64 get_iowait_time(int cpu) · line 104
Quoted 6 lines
fs/proc/stat.c +6
-0+show_irq_gap (p, i - next); +seq_put_decimal_ull (p," " ,kstat_irqs_usr (i)); + next = i +1 ; + } +show_irq_gap (p, nr_irqs - next); +}Every signed int can and should be unsigned int in this patch.
Indeed.
Quoted 16 lines · Thomas Gleixner
Waiman reported that on large systems with a large amount of interrupts the readout of
/proc/stattakes 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/statwith a high frequency.The reason for this is that interrupt statistics are accounted per cpu. So the
/proc/statlogic has to sum up the interrupt stats for each interrupt.The following series addresses this by making the interrupt statitics code in the core generate the sum directly and by making the loop in the
/proc/statread function smarter.Thanks,
tglx
Quoted 10 lines
8<----------------
fs/proc/stat.c | 28 +++++++++++++++++++++++++--- include/linux/irqdesc.h | 3 ++- kernel/irq/chip.c | 12 ++++++++++-- kernel/irq/internals.h | 8 +++++++- kernel/irq/irqdesc.c | 7 ++++++- 5 files changed, 50 insertions(+), 8 deletions(-)
For the whole series,
Quoted 5 lines · On Wed, 30 Jan 2019 13:31:30 +0100 Thomas Gleixner <[email protected]> wrote:
Waiman reported that on large systems with a large amount of interrupts the readout of
/proc/stattakes 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/statwith a high frequency.
Quoted 7 lines
The reason for this is that interrupt statistics are accounted per cpu. So the
/proc/statlogic has to sum up the interrupt stats for each interrupt.The following series addresses this by making the interrupt statitics code in the core generate the sum directly and by making the loop in the
/proc/statread function smarter.
Has the speedup been quantified?
Quoted 10 lines · Andrew Morton
Quoted 8 lines · On Wed, 30 Jan 2019 13:31:30 +0100 Thomas Gleixner <[email protected]> wrote:
Waiman reported that on large systems with a large amount of interrupts the readout of
/proc/stattakes 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/statwith a high frequency.The reason for this is that interrupt statistics are accounted per cpu. So the
/proc/statlogic has to sum up the interrupt stats for each interrupt.
Quoted 6 lines
Quoted 4 lines
The following series addresses this by making the interrupt statitics code in the core generate the sum directly and by making the loop in the
/proc/statread function smarter.Has the speedup been quantified?
Waiman should be able to provide numbers
Quoted 11 lines · Thomas Gleixner
Quoted 10 lines · Andrew Morton
Quoted 8 lines · On Wed, 30 Jan 2019 13:31:30 +0100 Thomas Gleixner <[email protected]> wrote:
Waiman reported that on large systems with a large amount of interrupts the readout of
/proc/stattakes 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/statwith a high frequency.The reason for this is that interrupt statistics are accounted per cpu. So the
/proc/statlogic has to sum up the interrupt stats for each interrupt.
Quoted 8 lines
Quoted 5 lines
Quoted 4 lines
The following series addresses this by making the interrupt statitics code in the core generate the sum directly and by making the loop in the
/proc/statread function smarter.Has the speedup been quantified?
Waiman should be able to provide numbers
Will run some performance test on the patch and give you guys the result sometime tomorrow.
Cheers,
Longman
Quoted 11 lines · Thomas Gleixner
Quoted 10 lines · Andrew Morton
Quoted 8 lines · On Wed, 30 Jan 2019 13:31:30 +0100 Thomas Gleixner <[email protected]> wrote:
Waiman reported that on large systems with a large amount of interrupts the readout of
/proc/stattakes 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/statwith a high frequency.The reason for this is that interrupt statistics are accounted per cpu. So the
/proc/statlogic has to sum up the interrupt stats for each interrupt.
Quoted 8 lines
Quoted 5 lines
Quoted 4 lines
The following series addresses this by making the interrupt statitics code in the core generate the sum directly and by making the loop in the
/proc/statread function smarter.Has the speedup been quantified?
Waiman should be able to provide numbers
On a 4-socket IvyBridge-EX system (60-core 120-thread) and 3016 irqs, I ran a test program that read /proc/stat 50,000 time. Before the patch, the elapsed time was 18.436s (sys 18.380s). After the patch, it was 3.769s (sys 3.742s). It was an almost 80% reduction in execution time. It was better than I expected. I like that.
Cheers,
Longman