[S390] fix mismatch in summation of I/O IRQ statistics
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Sun, 30 Oct 2011 14:16:04 +0000 (15:16 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Sun, 30 Oct 2011 14:16:15 +0000 (15:16 +0100)
Current IRQ statistics support does not show detail counts for I/O
interrupts which are processed internally only. The result is a
summation count which is way off such as this one:

           CPU0       CPU1       CPU2
I/O:       1331        710        442
[...]
QAI:         15         16         16   [I/O] QDIO Adapter Interrupt
QDI:          1          0          0   [I/O] QDIO Interrupt
DAS:        706        645        381   [I/O] DASD
C15:         26         10          0   [I/O] 3215
C70:          0          0          0   [I/O] 3270
TAP:          0          0          0   [I/O] Tape
VMR:          0          0          0   [I/O] Unit Record Devices
LCS:          0          0          0   [I/O] LCS
CLW:          0          0          0   [I/O] CLAW
CTC:          0          0          0   [I/O] CTC
APB:          0          0          0   [I/O] AP Bus

Fix this by moving I/O interrupt accounting into the common I/O layer.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
20 files changed:
arch/s390/include/asm/ccwdev.h
arch/s390/include/asm/irq.h
arch/s390/kernel/irq.c
drivers/s390/block/dasd.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_fba.c
drivers/s390/char/con3215.c
drivers/s390/char/raw3270.c
drivers/s390/char/tape_34xx.c
drivers/s390/char/tape_3590.c
drivers/s390/char/tape_core.c
drivers/s390/char/vmur.c
drivers/s390/cio/cio.c
drivers/s390/cio/device.c
drivers/s390/cio/device.h
drivers/s390/cio/io_sch.h
drivers/s390/cio/qdio_main.c
drivers/s390/net/claw.c
drivers/s390/net/ctcm_main.c
drivers/s390/net/lcs.c

index 623f2fb71774af7a352df0075f8253e776c15839..9381c92cc779b14afbf940fe5b4f56f54ad7d125 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
 #include <asm/fcx.h>
+#include <asm/irq.h>
 
 /* structs from asm/cio.h */
 struct irb;
@@ -127,6 +128,7 @@ enum uc_todo {
  * @restore: callback for restoring after hibernation
  * @uc_handler: callback for unit check handler
  * @driver: embedded device driver structure
+ * @int_class: interruption class to use for accounting interrupts
  */
 struct ccw_driver {
        struct ccw_device_id *ids;
@@ -144,6 +146,7 @@ struct ccw_driver {
        int (*restore)(struct ccw_device *);
        enum uc_todo (*uc_handler) (struct ccw_device *, struct irb *);
        struct device_driver driver;
+       enum interruption_class int_class;
 };
 
 extern struct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv,
index ba7b01c726a37eb024900e69db673542cc86db02..1f686059f2938c6ff8692fe3d4a8a0c221505e94 100644 (file)
@@ -17,8 +17,8 @@ enum interruption_class {
        EXTINT_SCP,
        EXTINT_IUC,
        EXTINT_CPM,
+       IOINT_CIO,
        IOINT_QAI,
-       IOINT_QDI,
        IOINT_DAS,
        IOINT_C15,
        IOINT_C70,
index 1f4050d45f78766c8fc43cc54b628b873e079b38..d382f9db3df5fa52e9c95fc531f9c1baed0f42fb 100644 (file)
@@ -42,8 +42,8 @@ static const struct irq_class intrclass_names[] = {
        {.name = "SCP", .desc = "[EXT] Service Call" },
        {.name = "IUC", .desc = "[EXT] IUCV" },
        {.name = "CPM", .desc = "[EXT] CPU Measurement" },
+       {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt" },
        {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" },
-       {.name = "QDI", .desc = "[I/O] QDIO Interrupt" },
        {.name = "DAS", .desc = "[I/O] DASD" },
        {.name = "C15", .desc = "[I/O] 3215" },
        {.name = "C70", .desc = "[I/O] 3270" },
index a1d3ddba99ccad4b90d4a9a06af1fd371b2d0033..46054c75cf3165ff02b33e1738c6d63dd9db2ed0 100644 (file)
@@ -11,7 +11,6 @@
 #define KMSG_COMPONENT "dasd"
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
-#include <linux/kernel_stat.h>
 #include <linux/kmod.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -1594,7 +1593,6 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
        unsigned long long now;
        int expires;
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_DAS]++;
        if (IS_ERR(irb)) {
                switch (PTR_ERR(irb)) {
                case -EIO:
index 6e835c9fdfcba9b5c6e9e501d6921aefa266c012..0e9c4dcf14527d0f6315996a617cecc869f65f36 100644 (file)
@@ -3998,6 +3998,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,
 };
 
 /*
index 4b71b116486838daea8132a832fcaa200f720016..a62a75358eb94ab7686373e7e166628e1eaf2773 100644 (file)
@@ -79,6 +79,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,
 };
 
 static void
index 694464c65fcdaaddd5c7c2b09514ca822e1606af..934458ad55e51782c7b595b48c5f3088393b8495 100644 (file)
@@ -9,7 +9,6 @@
  *           Dan Morrison, IBM Corporation <dmorriso@cse.buffalo.edu>
  */
 
-#include <linux/kernel_stat.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kdev_t.h>
@@ -362,7 +361,6 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,
        int cstat, dstat;
        int count;
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_C15]++;
        raw = dev_get_drvdata(&cdev->dev);
        req = (struct raw3215_req *) intparm;
        cstat = irb->scsw.cmd.cstat;
@@ -776,6 +774,7 @@ static struct ccw_driver raw3215_ccw_driver = {
        .freeze         = &raw3215_pm_stop,
        .thaw           = &raw3215_pm_start,
        .restore        = &raw3215_pm_start,
+       .int_class      = IOINT_C15,
 };
 
 #ifdef CONFIG_TN3215_CONSOLE
index 810ac38631c35ac919ba7fb110c80d38d75b260f..e5cb9248a442a5d67f574302aa76aa8904d1812a 100644 (file)
@@ -7,7 +7,6 @@
  *     Copyright IBM Corp. 2003, 2009
  */
 
-#include <linux/kernel_stat.h>
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -330,7 +329,6 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
        struct raw3270_request *rq;
        int rc;
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_C70]++;
        rp = dev_get_drvdata(&cdev->dev);
        if (!rp)
                return;
@@ -1398,6 +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,
 };
 
 static int
index 9eff2df70ddb2e579c0e2ff3472278df080178d2..934ef33eb9a478bf357341100468e89183e88429 100644 (file)
@@ -1330,6 +1330,7 @@ static struct ccw_driver tape_34xx_driver = {
        .set_online = tape_34xx_online,
        .set_offline = tape_generic_offline,
        .freeze = tape_generic_pm_suspend,
+       .int_class = IOINT_TAP,
 };
 
 static int
index a7d570728882cae1ce2443baa44044e1f824f00e..49c6aab7ad788d6bd9c3090af9d0931922f2c2e2 100644 (file)
@@ -1762,6 +1762,7 @@ static struct ccw_driver tape_3590_driver = {
        .set_offline = tape_generic_offline,
        .set_online = tape_3590_online,
        .freeze = tape_generic_pm_suspend,
+       .int_class = IOINT_TAP,
 };
 
 /*
index 7978a0adeaf3cd2781216d5c2d0fb13724282506..b3a3e8e8656e6db1be5e3a50b63056c43f0897f6 100644 (file)
@@ -14,7 +14,6 @@
 #define KMSG_COMPONENT "tape"
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
-#include <linux/kernel_stat.h>
 #include <linux/module.h>
 #include <linux/init.h>             // for kernel parameters
 #include <linux/kmod.h>             // for requesting modules
@@ -1115,7 +1114,6 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
        struct tape_request *request;
        int rc;
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_TAP]++;
        device = dev_get_drvdata(&cdev->dev);
        if (device == NULL) {
                return;
index f6b00c3df425d192da15790e75f16f6a6af09fb2..d291a54acfad5f94ec127cfdbff6e37d5b8cf85d 100644 (file)
@@ -11,7 +11,6 @@
 #define KMSG_COMPONENT "vmur"
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
-#include <linux/kernel_stat.h>
 #include <linux/cdev.h>
 #include <linux/slab.h>
 
@@ -74,6 +73,7 @@ static struct ccw_driver ur_driver = {
        .set_online     = ur_set_online,
        .set_offline    = ur_set_offline,
        .freeze         = ur_pm_suspend,
+       .int_class      = IOINT_VMR,
 };
 
 static DEFINE_MUTEX(vmur_mutex);
@@ -305,7 +305,6 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
 {
        struct urdev *urd;
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_VMR]++;
        TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
              intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
              irb->scsw.cmd.count);
index eb3140ee821eaab07fe43a2eb079909c11f3e362..5586c1376cb064e562e394f7893da99fa5953faf 100644 (file)
@@ -622,6 +622,7 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
                sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
                if (!sch) {
                        /* Clear pending interrupt condition. */
+                       kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
                        tsch(tpi_info->schid, irb);
                        continue;
                }
@@ -634,7 +635,10 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
                        /* Call interrupt handler if there is one. */
                        if (sch->driver && sch->driver->irq)
                                sch->driver->irq(sch);
-               }
+                       else
+                               kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
+               } else
+                       kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
                spin_unlock(sch->lock);
                /*
                 * Are more interrupts pending?
@@ -667,18 +671,23 @@ static int cio_tpi(void)
        tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id;
        if (tpi(NULL) != 1)
                return 0;
+       kstat_cpu(smp_processor_id()).irqs[IO_INTERRUPT]++;
        if (tpi_info->adapter_IO) {
                do_adapter_IO(tpi_info->isc);
                return 1;
        }
        irb = (struct irb *)&S390_lowcore.irb;
        /* Store interrupt response block to lowcore. */
-       if (tsch(tpi_info->schid, irb) != 0)
+       if (tsch(tpi_info->schid, irb) != 0) {
                /* Not status pending or not operational. */
+               kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
                return 1;
+       }
        sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
-       if (!sch)
+       if (!sch) {
+               kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
                return 1;
+       }
        irq_context = in_interrupt();
        if (!irq_context)
                local_bh_disable();
@@ -687,6 +696,8 @@ static int cio_tpi(void)
        memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
        if (sch->driver && sch->driver->irq)
                sch->driver->irq(sch);
+       else
+               kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
        spin_unlock(sch->lock);
        irq_exit();
        if (!irq_context)
index 8e04c00cf0ad10d522128b97e33cefcf3b870adf..d734f4a0ecac23cea1d821b316a563b71087ccc6 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/device.h>
 #include <linux/workqueue.h>
 #include <linux/timer.h>
+#include <linux/kernel_stat.h>
 
 #include <asm/ccwdev.h>
 #include <asm/cio.h>
@@ -747,6 +748,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
                                        struct ccw_device *cdev)
 {
        cdev->private->cdev = cdev;
+       cdev->private->int_class = IOINT_CIO;
        atomic_set(&cdev->private->onoff, 0);
        cdev->dev.parent = &sch->dev;
        cdev->dev.release = ccw_device_release;
@@ -1010,6 +1012,8 @@ static void io_subchannel_irq(struct subchannel *sch)
        CIO_TRACE_EVENT(6, dev_name(&sch->dev));
        if (cdev)
                dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
+       else
+               kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
 }
 
 void io_subchannel_init_config(struct subchannel *sch)
@@ -1621,6 +1625,7 @@ ccw_device_probe_console(void)
        memset(&console_private, 0, sizeof(struct ccw_device_private));
        console_cdev.private = &console_private;
        console_private.cdev = &console_cdev;
+       console_private.int_class = IOINT_CIO;
        ret = ccw_device_console_enable(&console_cdev, sch);
        if (ret) {
                cio_release_console();
@@ -1702,11 +1707,18 @@ ccw_device_probe (struct device *dev)
        int ret;
 
        cdev->drv = cdrv; /* to let the driver call _set_online */
+       /* Note: we interpret class 0 in this context as an uninitialized
+        * field since it translates to a non-I/O interrupt class. */
+       if (cdrv->int_class != 0)
+               cdev->private->int_class = cdrv->int_class;
+       else
+               cdev->private->int_class = IOINT_CIO;
 
        ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV;
 
        if (ret) {
                cdev->drv = NULL;
+               cdev->private->int_class = IOINT_CIO;
                return ret;
        }
 
@@ -1740,6 +1752,7 @@ ccw_device_remove (struct device *dev)
        }
        ccw_device_set_timeout(cdev, 0);
        cdev->drv = NULL;
+       cdev->private->int_class = IOINT_CIO;
        return 0;
 }
 
index 0b7245c72d5e290aeda9cd16ab1fabb681f784b2..179824b3082f731412b428e46fb71a8d1a912788 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/atomic.h>
 #include <linux/wait.h>
 #include <linux/notifier.h>
+#include <linux/kernel_stat.h>
 #include "io_sch.h"
 
 /*
@@ -56,7 +57,17 @@ extern fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS];
 static inline void
 dev_fsm_event(struct ccw_device *cdev, enum dev_event dev_event)
 {
-       dev_jumptable[cdev->private->state][dev_event](cdev, dev_event);
+       int state = cdev->private->state;
+
+       if (dev_event == DEV_EVENT_INTERRUPT) {
+               if (state == DEV_STATE_ONLINE)
+                       kstat_cpu(smp_processor_id()).
+                               irqs[cdev->private->int_class]++;
+               else if (state != DEV_STATE_CMFCHANGE &&
+                        state != DEV_STATE_CMFUPDATE)
+                       kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
+       }
+       dev_jumptable[state][dev_event](cdev, dev_event);
 }
 
 /*
index ba31ad88f4f7e3edf4d255e49ee8b457767beb0d..2ebb492a5c17dcb8a9e05fb23209162edb43d849 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/types.h>
 #include <asm/schid.h>
 #include <asm/ccwdev.h>
+#include <asm/irq.h>
 #include "css.h"
 #include "orb.h"
 
@@ -157,6 +158,7 @@ struct ccw_device_private {
        struct list_head cmb_list;      /* list of measured devices */
        u64 cmb_start_time;             /* clock value of cmb reset */
        void *cmb_wait;                 /* deferred cmb enable/disable */
+       enum interruption_class int_class;
 };
 
 static inline int rsch(struct subchannel_id schid)
index 6547ff46941015eccbc6256de9d8eda88282c95f..7ded1b26fd25615c0c001df9c4182410fadd203f 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/delay.h>
 #include <linux/gfp.h>
 #include <linux/io.h>
-#include <linux/kernel_stat.h>
 #include <linux/atomic.h>
 #include <asm/debug.h>
 #include <asm/qdio.h>
@@ -1128,7 +1127,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
                return;
        }
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_QDI]++;
        if (irq_ptr->perf_stat_enabled)
                irq_ptr->perf_stat.qdio_int++;
 
index f1fa2483ae6b9f02d065fb652732d6e081639eea..b41fae37d3afde703e225ec6f42ea59c94edf848 100644 (file)
@@ -63,7 +63,6 @@
 
 #define KMSG_COMPONENT "claw"
 
-#include <linux/kernel_stat.h>
 #include <asm/ccwdev.h>
 #include <asm/ccwgroup.h>
 #include <asm/debug.h>
@@ -291,6 +290,7 @@ static struct ccw_driver claw_ccw_driver = {
        .ids    = claw_ids,
        .probe  = ccwgroup_probe_ccwdev,
        .remove = ccwgroup_remove_ccwdev,
+       .int_class = IOINT_CLW,
 };
 
 static ssize_t
@@ -645,7 +645,6 @@ claw_irq_handler(struct ccw_device *cdev,
         struct claw_env  *p_env;
         struct chbk *p_ch_r=NULL;
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_CLW]++;
        CLAW_DBF_TEXT(4, trace, "clawirq");
         /* Bypass all 'unsolicited interrupts' */
        privptr = dev_get_drvdata(&cdev->dev);
index 426787efc49204c8a11036f0236a446c5e866dde..5cb93a8e340326b5a23a4d6e3c3e64a75b09749b 100644 (file)
@@ -24,7 +24,6 @@
 #define KMSG_COMPONENT "ctcm"
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
-#include <linux/kernel_stat.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -1203,7 +1202,6 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
        int cstat;
        int dstat;
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_CTC]++;
        CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
                "Enter %s(%s)", CTCM_FUNTAIL, dev_name(&cdev->dev));
 
@@ -1769,6 +1767,7 @@ static struct ccw_driver ctcm_ccw_driver = {
        .ids    = ctcm_ids,
        .probe  = ccwgroup_probe_ccwdev,
        .remove = ccwgroup_remove_ccwdev,
+       .int_class = IOINT_CTC,
 };
 
 static struct ccwgroup_driver ctcm_group_driver = {
index fb246b944b16e7219957f41ca200dc4459484da8..05fb3f7c72892cebe089d74b865f6207c8a70ef5 100644 (file)
@@ -26,7 +26,6 @@
 #define KMSG_COMPONENT         "lcs"
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
-#include <linux/kernel_stat.h>
 #include <linux/module.h>
 #include <linux/if.h>
 #include <linux/netdevice.h>
@@ -1399,7 +1398,6 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
        int rc, index;
        int cstat, dstat;
 
-       kstat_cpu(smp_processor_id()).irqs[IOINT_LCS]++;
        if (lcs_check_irb_error(cdev, irb))
                return;
 
@@ -2399,6 +2397,7 @@ static struct ccw_driver lcs_ccw_driver = {
        .ids    = lcs_ids,
        .probe  = ccwgroup_probe_ccwdev,
        .remove = ccwgroup_remove_ccwdev,
+       .int_class = IOINT_LCS,
 };
 
 /**