[S390] cio: failing set online/offline processing.
authorMichael Ernst <mernst@de.ibm.com>
Fri, 11 Sep 2009 08:28:21 +0000 (10:28 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 11 Sep 2009 08:29:38 +0000 (10:29 +0200)
When unit checks trigger sensing the device state is set to W4SENSE
until sense completion; then the device state is set back to
ONLINE. If a unit check occurs while set online or set offline
requests are processed then it might happen that the device's
temporary W4SENSE state causes these functions to terminate,
leaving the device in an inconsistent state when the state is set
back to ONLINE later on so that the device cannot be set online or
offline any longer.
To solve this, set online/offline and related rollback or error
routines are processed only if the device is in a final or
DISCONNECTED state.

Signed-off-by: Michael Ernst <mernst@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/device.c
drivers/s390/cio/device_fsm.c

index a7a340b1d71374217055697236d443f36d14309f..a50cfa51aa3cee6cd92cafcaba9e3ef2f9d8b5b7 100644 (file)
@@ -380,30 +380,34 @@ int ccw_device_set_offline(struct ccw_device *cdev)
        }
        cdev->online = 0;
        spin_lock_irq(cdev->ccwlock);
-       ret = ccw_device_offline(cdev);
-       if (ret == -ENODEV) {
-               if (cdev->private->state != DEV_STATE_NOT_OPER) {
-                       cdev->private->state = DEV_STATE_OFFLINE;
-                       dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
-               }
+       /* Wait until a final state or DISCONNECTED is reached */
+       while (!dev_fsm_final_state(cdev) &&
+              cdev->private->state != DEV_STATE_DISCONNECTED) {
                spin_unlock_irq(cdev->ccwlock);
-               /* Give up reference from ccw_device_set_online(). */
-               put_device(&cdev->dev);
-               return ret;
+               wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
+                          cdev->private->state == DEV_STATE_DISCONNECTED));
+               spin_lock_irq(cdev->ccwlock);
        }
+       ret = ccw_device_offline(cdev);
+       if (ret)
+               goto error;
        spin_unlock_irq(cdev->ccwlock);
-       if (ret == 0) {
-               wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
-               /* Give up reference from ccw_device_set_online(). */
-               put_device(&cdev->dev);
-       } else {
-               CIO_MSG_EVENT(0, "ccw_device_offline returned %d, "
-                             "device 0.%x.%04x\n",
-                             ret, cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno);
-               cdev->online = 1;
-       }
-       return ret;
+       wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
+                  cdev->private->state == DEV_STATE_DISCONNECTED));
+       /* Give up reference from ccw_device_set_online(). */
+       put_device(&cdev->dev);
+       return 0;
+
+error:
+       CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device 0.%x.%04x\n",
+                     ret, cdev->private->dev_id.ssid,
+                     cdev->private->dev_id.devno);
+       cdev->private->state = DEV_STATE_OFFLINE;
+       dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
+       spin_unlock_irq(cdev->ccwlock);
+       /* Give up reference from ccw_device_set_online(). */
+       put_device(&cdev->dev);
+       return -ENODEV;
 }
 
 /**
@@ -421,6 +425,7 @@ int ccw_device_set_offline(struct ccw_device *cdev)
 int ccw_device_set_online(struct ccw_device *cdev)
 {
        int ret;
+       int ret2;
 
        if (!cdev)
                return -ENODEV;
@@ -444,28 +449,53 @@ int ccw_device_set_online(struct ccw_device *cdev)
                put_device(&cdev->dev);
                return ret;
        }
-       if (cdev->private->state != DEV_STATE_ONLINE) {
+       spin_lock_irq(cdev->ccwlock);
+       /* Check if online processing was successful */
+       if ((cdev->private->state != DEV_STATE_ONLINE) &&
+           (cdev->private->state != DEV_STATE_W4SENSE)) {
+               spin_unlock_irq(cdev->ccwlock);
                /* Give up online reference since onlining failed. */
                put_device(&cdev->dev);
                return -ENODEV;
        }
-       if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) {
-               cdev->online = 1;
-               return 0;
-       }
+       spin_unlock_irq(cdev->ccwlock);
+       if (cdev->drv->set_online)
+               ret = cdev->drv->set_online(cdev);
+       if (ret)
+               goto rollback;
+       cdev->online = 1;
+       return 0;
+
+rollback:
        spin_lock_irq(cdev->ccwlock);
-       ret = ccw_device_offline(cdev);
+       /* Wait until a final state or DISCONNECTED is reached */
+       while (!dev_fsm_final_state(cdev) &&
+              cdev->private->state != DEV_STATE_DISCONNECTED) {
+               spin_unlock_irq(cdev->ccwlock);
+               wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
+                          cdev->private->state == DEV_STATE_DISCONNECTED));
+               spin_lock_irq(cdev->ccwlock);
+       }
+       ret2 = ccw_device_offline(cdev);
+       if (ret2)
+               goto error;
        spin_unlock_irq(cdev->ccwlock);
-       if (ret == 0)
-               wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
-       else
-               CIO_MSG_EVENT(0, "ccw_device_offline returned %d, "
-                             "device 0.%x.%04x\n",
-                             ret, cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno);
+       wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
+                  cdev->private->state == DEV_STATE_DISCONNECTED));
        /* Give up online reference since onlining failed. */
        put_device(&cdev->dev);
-       return (ret == 0) ? -ENODEV : ret;
+       return ret;
+
+error:
+       CIO_MSG_EVENT(0, "rollback ccw_device_offline returned %d, "
+                     "device 0.%x.%04x\n",
+                     ret2, cdev->private->dev_id.ssid,
+                     cdev->private->dev_id.devno);
+       cdev->private->state = DEV_STATE_OFFLINE;
+       spin_unlock_irq(cdev->ccwlock);
+       /* Give up online reference since onlining failed. */
+       put_device(&cdev->dev);
+       return ret;
 }
 
 static int online_store_handle_offline(struct ccw_device *cdev)
index 9c1452a6b9a124240ac77c0f5fad9c918d0066ed..e728ce447f6ee107e5482886cb0c8f41115cb363 100644 (file)
@@ -905,6 +905,8 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
        }
 call_handler:
        cdev->private->state = DEV_STATE_ONLINE;
+       /* In case sensing interfered with setting the device online */
+       wake_up(&cdev->private->wait_q);
        /* Call the handler. */
        if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify)
                /* Start delayed path verification. */