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

Commit 420f42ec authored by Heiko Carstens's avatar Heiko Carstens Committed by Martin Schwidefsky
Browse files

s390/irq: remove split irq fields from /proc/stat



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: 052ff461 "[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: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent add9bde2
Loading
Loading
Loading
Loading
+47 −30
Original line number Diff line number Diff line
@@ -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;
+73 −48
Original line number Diff line number Diff line
@@ -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"},
};

@@ -68,28 +89,32 @@ 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);
	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;
}
+1 −1
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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;
Loading