Skip to content

Commit

Permalink
s390/irq: remove split irq fields from /proc/stat
Browse files Browse the repository at this point in the history
Now that irq sum accounting for /proc/stat's "intr" line works again we
have the oddity that the sum field (first field) contains only the sum
of the second (external irqs) and third field (I/O interrupts).
The reason for that is that these two fields are already sums of all other
fields. So if we would sum up everything we would count every interrupt
twice.
This is broken since the split interrupt accounting was merged two years
ago: 052ff46 "[S390] irq: have detailed
statistics for interrupt types".
To fix this remove the split interrupt fields from /proc/stat's "intr"
line again and only have them in /proc/interrupts.

This restores the old behaviour, seems to be the only sane fix and mimics
a behaviour from other architectures where /proc/interrupts also contains
more than /proc/stat's "intr" line does.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Heiko Carstens authored and Martin Schwidefsky committed Jan 8, 2013
1 parent add9bde commit 420f42e
Show file tree
Hide file tree
Showing 31 changed files with 162 additions and 121 deletions.
77 changes: 47 additions & 30 deletions arch/s390/include/asm/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,60 @@
#define _ASM_IRQ_H

#include <linux/hardirq.h>
#include <linux/percpu.h>
#include <linux/cache.h>
#include <linux/types.h>

enum interruption_class {
enum interruption_main_class {
EXTERNAL_INTERRUPT,
IO_INTERRUPT,
EXTINT_CLK,
EXTINT_EXC,
EXTINT_EMS,
EXTINT_TMR,
EXTINT_TLA,
EXTINT_PFL,
EXTINT_DSD,
EXTINT_VRT,
EXTINT_SCP,
EXTINT_IUC,
EXTINT_CMS,
EXTINT_CMC,
EXTINT_CMR,
IOINT_CIO,
IOINT_QAI,
IOINT_DAS,
IOINT_C15,
IOINT_C70,
IOINT_TAP,
IOINT_VMR,
IOINT_LCS,
IOINT_CLW,
IOINT_CTC,
IOINT_APB,
IOINT_ADM,
IOINT_CSC,
IOINT_PCI,
IOINT_MSI,
NR_IRQS
};

enum interruption_class {
IRQEXT_CLK,
IRQEXT_EXC,
IRQEXT_EMS,
IRQEXT_TMR,
IRQEXT_TLA,
IRQEXT_PFL,
IRQEXT_DSD,
IRQEXT_VRT,
IRQEXT_SCP,
IRQEXT_IUC,
IRQEXT_CMS,
IRQEXT_CMC,
IRQEXT_CMR,
IRQIO_CIO,
IRQIO_QAI,
IRQIO_DAS,
IRQIO_C15,
IRQIO_C70,
IRQIO_TAP,
IRQIO_VMR,
IRQIO_LCS,
IRQIO_CLW,
IRQIO_CTC,
IRQIO_APB,
IRQIO_ADM,
IRQIO_CSC,
IRQIO_PCI,
IRQIO_MSI,
NMI_NMI,
NR_IRQS,
NR_ARCH_IRQS
};

struct irq_stat {
unsigned int irqs[NR_ARCH_IRQS];
};

DECLARE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat);

static __always_inline void inc_irq_stat(enum interruption_class irq)
{
__get_cpu_var(irq_stat).irqs[irq]++;
}

struct ext_code {
unsigned short subcode;
unsigned short code;
Expand Down
121 changes: 73 additions & 48 deletions arch/s390/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,63 @@
#include <asm/irq.h>
#include "entry.h"

DEFINE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat);
EXPORT_PER_CPU_SYMBOL_GPL(irq_stat);

struct irq_class {
char *name;
char *desc;
};

static const struct irq_class intrclass_names[] = {
/*
* The list of "main" irq classes on s390. This is the list of interrrupts
* that appear both in /proc/stat ("intr" line) and /proc/interrupts.
* Historically only external and I/O interrupts have been part of /proc/stat.
* We can't add the split external and I/O sub classes since the first field
* in the "intr" line in /proc/stat is supposed to be the sum of all other
* fields.
* Since the external and I/O interrupt fields are already sums we would end
* up with having a sum which accounts each interrupt twice.
*/
static const struct irq_class irqclass_main_desc[NR_IRQS] = {
[EXTERNAL_INTERRUPT] = {.name = "EXT"},
[IO_INTERRUPT] = {.name = "I/O"},
[EXTINT_CLK] = {.name = "CLK", .desc = "[EXT] Clock Comparator"},
[EXTINT_EXC] = {.name = "EXC", .desc = "[EXT] External Call"},
[EXTINT_EMS] = {.name = "EMS", .desc = "[EXT] Emergency Signal"},
[EXTINT_TMR] = {.name = "TMR", .desc = "[EXT] CPU Timer"},
[EXTINT_TLA] = {.name = "TAL", .desc = "[EXT] Timing Alert"},
[EXTINT_PFL] = {.name = "PFL", .desc = "[EXT] Pseudo Page Fault"},
[EXTINT_DSD] = {.name = "DSD", .desc = "[EXT] DASD Diag"},
[EXTINT_VRT] = {.name = "VRT", .desc = "[EXT] Virtio"},
[EXTINT_SCP] = {.name = "SCP", .desc = "[EXT] Service Call"},
[EXTINT_IUC] = {.name = "IUC", .desc = "[EXT] IUCV"},
[EXTINT_CMS] = {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"},
[EXTINT_CMC] = {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"},
[EXTINT_CMR] = {.name = "CMR", .desc = "[EXT] CPU-Measurement: RI"},
[IOINT_CIO] = {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"},
[IOINT_QAI] = {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"},
[IOINT_DAS] = {.name = "DAS", .desc = "[I/O] DASD"},
[IOINT_C15] = {.name = "C15", .desc = "[I/O] 3215"},
[IOINT_C70] = {.name = "C70", .desc = "[I/O] 3270"},
[IOINT_TAP] = {.name = "TAP", .desc = "[I/O] Tape"},
[IOINT_VMR] = {.name = "VMR", .desc = "[I/O] Unit Record Devices"},
[IOINT_LCS] = {.name = "LCS", .desc = "[I/O] LCS"},
[IOINT_CLW] = {.name = "CLW", .desc = "[I/O] CLAW"},
[IOINT_CTC] = {.name = "CTC", .desc = "[I/O] CTC"},
[IOINT_APB] = {.name = "APB", .desc = "[I/O] AP Bus"},
[IOINT_ADM] = {.name = "ADM", .desc = "[I/O] EADM Subchannel"},
[IOINT_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"},
[IOINT_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" },
[IOINT_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" },
[IO_INTERRUPT] = {.name = "I/O"}
};

/*
* The list of split external and I/O interrupts that appear only in
* /proc/interrupts.
* In addition this list contains non external / I/O events like NMIs.
*/
static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = {
[IRQEXT_CLK] = {.name = "CLK", .desc = "[EXT] Clock Comparator"},
[IRQEXT_EXC] = {.name = "EXC", .desc = "[EXT] External Call"},
[IRQEXT_EMS] = {.name = "EMS", .desc = "[EXT] Emergency Signal"},
[IRQEXT_TMR] = {.name = "TMR", .desc = "[EXT] CPU Timer"},
[IRQEXT_TLA] = {.name = "TAL", .desc = "[EXT] Timing Alert"},
[IRQEXT_PFL] = {.name = "PFL", .desc = "[EXT] Pseudo Page Fault"},
[IRQEXT_DSD] = {.name = "DSD", .desc = "[EXT] DASD Diag"},
[IRQEXT_VRT] = {.name = "VRT", .desc = "[EXT] Virtio"},
[IRQEXT_SCP] = {.name = "SCP", .desc = "[EXT] Service Call"},
[IRQEXT_IUC] = {.name = "IUC", .desc = "[EXT] IUCV"},
[IRQEXT_CMS] = {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"},
[IRQEXT_CMC] = {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"},
[IRQEXT_CMR] = {.name = "CMR", .desc = "[EXT] CPU-Measurement: RI"},
[IRQIO_CIO] = {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"},
[IRQIO_QAI] = {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"},
[IRQIO_DAS] = {.name = "DAS", .desc = "[I/O] DASD"},
[IRQIO_C15] = {.name = "C15", .desc = "[I/O] 3215"},
[IRQIO_C70] = {.name = "C70", .desc = "[I/O] 3270"},
[IRQIO_TAP] = {.name = "TAP", .desc = "[I/O] Tape"},
[IRQIO_VMR] = {.name = "VMR", .desc = "[I/O] Unit Record Devices"},
[IRQIO_LCS] = {.name = "LCS", .desc = "[I/O] LCS"},
[IRQIO_CLW] = {.name = "CLW", .desc = "[I/O] CLAW"},
[IRQIO_CTC] = {.name = "CTC", .desc = "[I/O] CTC"},
[IRQIO_APB] = {.name = "APB", .desc = "[I/O] AP Bus"},
[IRQIO_ADM] = {.name = "ADM", .desc = "[I/O] EADM Subchannel"},
[IRQIO_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"},
[IRQIO_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" },
[IRQIO_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" },
[NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"},
};

Expand All @@ -68,30 +89,34 @@ static const struct irq_class intrclass_names[] = {
*/
int show_interrupts(struct seq_file *p, void *v)
{
int i = *(loff_t *) v, j;
int irq = *(loff_t *) v;
int cpu;

get_online_cpus();
if (i == 0) {
if (irq == 0) {
seq_puts(p, " ");
for_each_online_cpu(j)
seq_printf(p, "CPU%d ",j);
for_each_online_cpu(cpu)
seq_printf(p, "CPU%d ", cpu);
seq_putc(p, '\n');
}

if (i < NR_IRQS) {
seq_printf(p, "%s: ", intrclass_names[i].name);
#ifndef CONFIG_SMP
seq_printf(p, "%10u ", kstat_irqs(i));
#else
for_each_online_cpu(j)
seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
#endif
if (intrclass_names[i].desc)
seq_printf(p, " %s", intrclass_names[i].desc);
seq_putc(p, '\n');
}
if (irq < NR_IRQS) {
seq_printf(p, "%s: ", irqclass_main_desc[irq].name);
for_each_online_cpu(cpu)
seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[irq]);
seq_putc(p, '\n');
goto skip_arch_irqs;
}
for (irq = 0; irq < NR_ARCH_IRQS; irq++) {
seq_printf(p, "%s: ", irqclass_sub_desc[irq].name);
for_each_online_cpu(cpu)
seq_printf(p, "%10u ", per_cpu(irq_stat, cpu).irqs[irq]);
if (irqclass_sub_desc[irq].desc)
seq_printf(p, " %s", irqclass_sub_desc[irq].desc);
seq_putc(p, '\n');
}
skip_arch_irqs:
put_online_cpus();
return 0;
return 0;
}

/*
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/kernel/nmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
int umode;

nmi_enter();
kstat_cpu(smp_processor_id()).irqs[NMI_NMI]++;
inc_irq_stat(NMI_NMI);
mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
mcck = &__get_cpu_var(cpu_mcck);
umode = user_mode(regs);
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/kernel/perf_cpum_cf.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
if (!(alert & CPU_MF_INT_CF_MASK))
return;

kstat_cpu(smp_processor_id()).irqs[EXTINT_CMC]++;
inc_irq_stat(IRQEXT_CMC);
cpuhw = &__get_cpu_var(cpu_hw_events);

/* Measurement alerts are shared and might happen when the PMU
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/kernel/runtime_instr.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ static void runtime_instr_int_handler(struct ext_code ext_code,
if (!(param32 & CPU_MF_INT_RI_MASK))
return;

kstat_cpu(smp_processor_id()).irqs[EXTINT_CMR]++;
inc_irq_stat(IRQEXT_CMR);

if (!current->thread.ri_cb)
return;
Expand Down
4 changes: 2 additions & 2 deletions arch/s390/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,9 @@ static void do_ext_call_interrupt(struct ext_code ext_code,

cpu = smp_processor_id();
if (ext_code.code == 0x1202)
kstat_cpu(cpu).irqs[EXTINT_EXC]++;
inc_irq_stat(IRQEXT_EXC);
else
kstat_cpu(cpu).irqs[EXTINT_EMS]++;
inc_irq_stat(IRQEXT_EMS);
/*
* handle bit signal external calls
*/
Expand Down
4 changes: 2 additions & 2 deletions arch/s390/kernel/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ static void clock_comparator_interrupt(struct ext_code ext_code,
unsigned int param32,
unsigned long param64)
{
kstat_cpu(smp_processor_id()).irqs[EXTINT_CLK]++;
inc_irq_stat(IRQEXT_CLK);
if (S390_lowcore.clock_comparator == -1ULL)
set_clock_comparator(S390_lowcore.clock_comparator);
}
Expand All @@ -179,7 +179,7 @@ static void stp_timing_alert(struct stp_irq_parm *);
static void timing_alert_interrupt(struct ext_code ext_code,
unsigned int param32, unsigned long param64)
{
kstat_cpu(smp_processor_id()).irqs[EXTINT_TLA]++;
inc_irq_stat(IRQEXT_TLA);
if (param32 & 0x00c40000)
etr_timing_alert((struct etr_irq_parm *) &param32);
if (param32 & 0x00038000)
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ static void pfault_interrupt(struct ext_code ext_code,
subcode = ext_code.subcode;
if ((subcode & 0xff00) != __SUBCODE_MASK)
return;
kstat_cpu(smp_processor_id()).irqs[EXTINT_PFL]++;
inc_irq_stat(IRQEXT_PFL);
/* Get the token (= pid of the affected task). */
pid = sizeof(void *) == 4 ? param32 : param64;
rcu_read_lock();
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/oprofile/hwsampler.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ static void hws_ext_handler(struct ext_code ext_code,
if (!(param32 & CPU_MF_INT_SF_MASK))
return;

kstat_cpu(smp_processor_id()).irqs[EXTINT_CMS]++;
inc_irq_stat(IRQEXT_CMS);
atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32);

if (hws_wq)
Expand Down
4 changes: 2 additions & 2 deletions arch/s390/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ static void zpci_irq_handler(void *dont, void *need)
int rescan = 0, max = aisb_max;
struct zdev_irq_map *imap;

kstat_cpu(smp_processor_id()).irqs[IOINT_PCI]++;
inc_irq_stat(IRQIO_PCI);
sbit = start;

scan:
Expand All @@ -452,7 +452,7 @@ static void zpci_irq_handler(void *dont, void *need)
/* find vector bit */
imap = bucket->imap[sbit];
for_each_set_bit_left(mbit, &imap->aibv, imap->msi_vecs) {
kstat_cpu(smp_processor_id()).irqs[IOINT_MSI]++;
inc_irq_stat(IRQIO_MSI);
clear_bit(63 - mbit, &imap->aibv);

spin_lock(&imap->lock);
Expand Down
2 changes: 1 addition & 1 deletion drivers/s390/block/dasd_diag.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ static void dasd_ext_handler(struct ext_code ext_code,
default:
return;
}
kstat_cpu(smp_processor_id()).irqs[EXTINT_DSD]++;
inc_irq_stat(IRQEXT_DSD);
if (!ip) { /* no intparm: unsolicited interrupt */
DBF_EVENT(DBF_NOTICE, "%s", "caught unsolicited "
"interrupt");
Expand Down
2 changes: 1 addition & 1 deletion drivers/s390/block/dasd_eckd.c
Original file line number Diff line number Diff line change
Expand Up @@ -4274,7 +4274,7 @@ static struct ccw_driver dasd_eckd_driver = {
.thaw = dasd_generic_restore_device,
.restore = dasd_generic_restore_device,
.uc_handler = dasd_generic_uc_handler,
.int_class = IOINT_DAS,
.int_class = IRQIO_DAS,
};

/*
Expand Down
2 changes: 1 addition & 1 deletion drivers/s390/block/dasd_fba.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ static struct ccw_driver dasd_fba_driver = {
.freeze = dasd_generic_pm_freeze,
.thaw = dasd_generic_restore_device,
.restore = dasd_generic_restore_device,
.int_class = IOINT_DAS,
.int_class = IRQIO_DAS,
};

static void
Expand Down
2 changes: 1 addition & 1 deletion drivers/s390/char/con3215.c
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ static struct ccw_driver raw3215_ccw_driver = {
.freeze = &raw3215_pm_stop,
.thaw = &raw3215_pm_start,
.restore = &raw3215_pm_start,
.int_class = IOINT_C15,
.int_class = IRQIO_C15,
};

#ifdef CONFIG_TN3215_CONSOLE
Expand Down
2 changes: 1 addition & 1 deletion drivers/s390/char/raw3270.c
Original file line number Diff line number Diff line change
Expand Up @@ -1396,7 +1396,7 @@ static struct ccw_driver raw3270_ccw_driver = {
.freeze = &raw3270_pm_stop,
.thaw = &raw3270_pm_start,
.restore = &raw3270_pm_start,
.int_class = IOINT_C70,
.int_class = IRQIO_C70,
};

static int
Expand Down
4 changes: 2 additions & 2 deletions drivers/s390/char/sclp.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ static void sclp_interrupt_handler(struct ext_code ext_code,
u32 finished_sccb;
u32 evbuf_pending;

kstat_cpu(smp_processor_id()).irqs[EXTINT_SCP]++;
inc_irq_stat(IRQEXT_SCP);
spin_lock(&sclp_lock);
finished_sccb = param32 & 0xfffffff8;
evbuf_pending = param32 & 0x3;
Expand Down Expand Up @@ -813,7 +813,7 @@ static void sclp_check_handler(struct ext_code ext_code,
{
u32 finished_sccb;

kstat_cpu(smp_processor_id()).irqs[EXTINT_SCP]++;
inc_irq_stat(IRQEXT_SCP);
finished_sccb = param32 & 0xfffffff8;
/* Is this the interrupt we are waiting for? */
if (finished_sccb == 0)
Expand Down
Loading

0 comments on commit 420f42e

Please sign in to comment.