Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / s390 / cio / device_fsm.c
index 9b7f6f5..49ec562 100644 (file)
@@ -4,16 +4,18 @@
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *                      IBM Corporation
- *    Author(s): Cornelia Huck(cohuck@de.ibm.com)
+ *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
  *              Martin Schwidefsky (schwidefsky@de.ibm.com)
  */
 
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
 
 #include <asm/ccwdev.h>
-#include <asm/qdio.h>
+#include <asm/cio.h>
 
 #include "cio.h"
 #include "cio_debug.h"
@@ -21,7 +23,6 @@
 #include "device.h"
 #include "chsc.h"
 #include "ioasm.h"
-#include "qdio.h"
 
 int
 device_is_online(struct subchannel *sch)
@@ -132,7 +133,7 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
        int ret;
 
        sch = to_subchannel(cdev->dev.parent);
-       ret = stsch(sch->irq, &sch->schib);
+       ret = stsch(sch->schid, &sch->schib);
        if (ret || !sch->schib.pmcw.dnv)
                return -ENODEV; 
        if (!sch->schib.pmcw.ena || sch->schib.scsw.actl == 0)
@@ -230,11 +231,14 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
         * through ssch() and the path information is up to date.
         */
        old_lpm = sch->lpm;
-       stsch(sch->irq, &sch->schib);
+       stsch(sch->schid, &sch->schib);
        sch->lpm = sch->schib.pmcw.pim &
                sch->schib.pmcw.pam &
                sch->schib.pmcw.pom &
                sch->opm;
+       /* Check since device may again have become not operational. */
+       if (!sch->schib.pmcw.dnv)
+               state = DEV_STATE_NOT_OPER;
        if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID)
                /* Force reprobe on all chpids. */
                old_lpm = 0;
@@ -253,8 +257,9 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
        switch (state) {
        case DEV_STATE_NOT_OPER:
                CIO_DEBUG(KERN_WARNING, 2,
-                         "SenseID : unknown device %04x on subchannel %04x\n",
-                         cdev->private->devno, sch->irq);
+                         "SenseID : unknown device %04x on subchannel "
+                         "0.%x.%04x\n", cdev->private->devno,
+                         sch->schid.ssid, sch->schid.sch_no);
                break;
        case DEV_STATE_OFFLINE:
                if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
@@ -278,16 +283,18 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
                        return;
                }
                /* Issue device info message. */
-               CIO_DEBUG(KERN_INFO, 2, "SenseID : device %04x reports: "
+               CIO_DEBUG(KERN_INFO, 2, "SenseID : device 0.%x.%04x reports: "
                          "CU  Type/Mod = %04X/%02X, Dev Type/Mod = "
-                         "%04X/%02X\n", cdev->private->devno,
+                         "%04X/%02X\n",
+                         cdev->private->ssid, cdev->private->devno,
                          cdev->id.cu_type, cdev->id.cu_model,
                          cdev->id.dev_type, cdev->id.dev_model);
                break;
        case DEV_STATE_BOXED:
                CIO_DEBUG(KERN_WARNING, 2,
-                         "SenseID : boxed device %04x on subchannel %04x\n",
-                         cdev->private->devno, sch->irq);
+                         "SenseID : boxed device %04x on subchannel "
+                         "0.%x.%04x\n", cdev->private->devno,
+                         sch->schid.ssid, sch->schid.sch_no);
                break;
        }
        cdev->private->state = state;
@@ -355,7 +362,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
        if (state == DEV_STATE_BOXED)
                CIO_DEBUG(KERN_WARNING, 2,
                          "Boxed device %04x on subchannel %04x\n",
-                         cdev->private->devno, sch->irq);
+                         cdev->private->devno, sch->schid.sch_no);
 
        if (cdev->private->flags.donotify) {
                cdev->private->flags.donotify = 0;
@@ -588,7 +595,7 @@ ccw_device_offline(struct ccw_device *cdev)
        struct subchannel *sch;
 
        sch = to_subchannel(cdev->dev.parent);
-       if (stsch(sch->irq, &sch->schib) || !sch->schib.pmcw.dnv)
+       if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
                return -ENODEV;
        if (cdev->private->state != DEV_STATE_ONLINE) {
                if (sch->schib.scsw.actl != 0)
@@ -707,7 +714,7 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
         * Since we might not just be coming from an interrupt from the
         * subchannel we have to update the schib.
         */
-       stsch(sch->irq, &sch->schib);
+       stsch(sch->schid, &sch->schib);
 
        if (sch->schib.scsw.actl != 0 ||
            (cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
@@ -742,7 +749,7 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
                        /* Unit check but no sense data. Need basic sense. */
                        if (ccw_device_do_sense(cdev, irb) != 0)
                                goto call_handler_unsol;
-                       memcpy(irb, &cdev->private->irb, sizeof(struct irb));
+                       memcpy(&cdev->private->irb, irb, sizeof(struct irb));
                        cdev->private->state = DEV_STATE_W4SENSE;
                        cdev->private->intparm = 0;
                        return;
@@ -820,6 +827,17 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
                }
                return;
        }
+       /*
+        * Check if a halt or clear has been issued in the meanwhile. If yes,
+        * only deliver the halt/clear interrupt to the device driver as if it
+        * had killed the original request.
+        */
+       if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
+               cdev->private->flags.dosense = 0;
+               memset(&cdev->private->irb, 0, sizeof(struct irb));
+               ccw_device_accumulate_irb(cdev, irb);
+               goto call_handler;
+       }
        /* Add basic sense info to irb. */
        ccw_device_accumulate_basic_sense(cdev, irb);
        if (cdev->private->flags.dosense) {
@@ -827,6 +845,7 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
                ccw_device_do_sense(cdev, irb);
                return;
        }
+call_handler:
        cdev->private->state = DEV_STATE_ONLINE;
        /* Call the handler. */
        if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify)
@@ -919,7 +938,7 @@ ccw_device_wait4io_irq(struct ccw_device *cdev, enum dev_event dev_event)
 
        /* Iff device is idle, reset timeout. */
        sch = to_subchannel(cdev->dev.parent);
-       if (!stsch(sch->irq, &sch->schib))
+       if (!stsch(sch->schid, &sch->schib))
                if (sch->schib.scsw.actl == 0)
                        ccw_device_set_timeout(cdev, 0);
        /* Call the handler. */
@@ -1031,7 +1050,7 @@ device_trigger_reprobe(struct subchannel *sch)
                return;
 
        /* Update some values. */
-       if (stsch(sch->irq, &sch->schib))
+       if (stsch(sch->schid, &sch->schib))
                return;
 
        /*