]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
[S390] cio: fix I/O cancel function
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Mon, 25 Oct 2010 14:10:44 +0000 (16:10 +0200)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Mon, 25 Oct 2010 14:10:20 +0000 (16:10 +0200)
Function ccw_device_cancel_halt_clear may cause an unexpected kernel
panic if a clear function is currently active at the subchannel for
which it is called. In that case, the iretry counter used to determine
the number of retries is never initialized, leading to an immediate
failure of the function which results in a kernel panic.

Fix this by initializing the iretry counter when the function is
first called. Also replace the kernel panic with a return code: a
single malfunctioning I/O device should not automatically cause a
system-wide kernel panic.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/device.c
drivers/s390/cio/device_fsm.c

index 881bdfd99140c03fa6f0c75aa8c290ea46c37ccd..2ff8a22d42574242eb0a07c1ab1d38f73cc81d6b 100644 (file)
@@ -1205,6 +1205,7 @@ static void io_subchannel_quiesce(struct subchannel *sch)
                cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
        while (ret == -EBUSY) {
                cdev->private->state = DEV_STATE_QUIESCE;
                cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
        while (ret == -EBUSY) {
                cdev->private->state = DEV_STATE_QUIESCE;
+               cdev->private->iretry = 255;
                ret = ccw_device_cancel_halt_clear(cdev);
                if (ret == -EBUSY) {
                        ccw_device_set_timeout(cdev, HZ/10);
                ret = ccw_device_cancel_halt_clear(cdev);
                if (ret == -EBUSY) {
                        ccw_device_set_timeout(cdev, HZ/10);
index 4395c01a9daca092d1f151f8cc31727f91562b2e..a845695ac314dc5dd7e1c3733bacd35216a88235 100644 (file)
@@ -174,7 +174,10 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
                ret = cio_clear (sch);
                return (ret == 0) ? -EBUSY : ret;
        }
                ret = cio_clear (sch);
                return (ret == 0) ? -EBUSY : ret;
        }
-       panic("Can't stop i/o on subchannel.\n");
+       /* Function was unsuccessful */
+       CIO_MSG_EVENT(0, "0.%x.%04x: could not stop I/O\n",
+                     cdev->private->dev_id.ssid, cdev->private->dev_id.devno);
+       return -EIO;
 }
 
 void ccw_device_update_sense_data(struct ccw_device *cdev)
 }
 
 void ccw_device_update_sense_data(struct ccw_device *cdev)
@@ -766,13 +769,14 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
        int ret;
 
        ccw_device_set_timeout(cdev, 0);
        int ret;
 
        ccw_device_set_timeout(cdev, 0);
+       cdev->private->iretry = 255;
        ret = ccw_device_cancel_halt_clear(cdev);
        if (ret == -EBUSY) {
                ccw_device_set_timeout(cdev, 3*HZ);
                cdev->private->state = DEV_STATE_TIMEOUT_KILL;
                return;
        }
        ret = ccw_device_cancel_halt_clear(cdev);
        if (ret == -EBUSY) {
                ccw_device_set_timeout(cdev, 3*HZ);
                cdev->private->state = DEV_STATE_TIMEOUT_KILL;
                return;
        }
-       if (ret == -ENODEV)
+       if (ret)
                dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
        else if (cdev->handler)
                cdev->handler(cdev, cdev->private->intparm,
                dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
        else if (cdev->handler)
                cdev->handler(cdev, cdev->private->intparm,
@@ -869,6 +873,7 @@ void ccw_device_kill_io(struct ccw_device *cdev)
 {
        int ret;
 
 {
        int ret;
 
+       cdev->private->iretry = 255;
        ret = ccw_device_cancel_halt_clear(cdev);
        if (ret == -EBUSY) {
                ccw_device_set_timeout(cdev, 3*HZ);
        ret = ccw_device_cancel_halt_clear(cdev);
        if (ret == -EBUSY) {
                ccw_device_set_timeout(cdev, 3*HZ);