diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-12-07 12:51:41 +0100 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 12:51:33 +0100 |
commit | d40f7b75a23d1e59b6ec9d6701231fd4c6992ac6 (patch) | |
tree | 74dc2947aea7aadc5a2ec81e70c5e41111f5d3ae | |
parent | de1b04388f63cbddf91d9f6c50c29be7232881ca (diff) | |
download | talos-obmc-linux-d40f7b75a23d1e59b6ec9d6701231fd4c6992ac6.tar.gz talos-obmc-linux-d40f7b75a23d1e59b6ec9d6701231fd4c6992ac6.zip |
[S390] cio: dont unregister a busy device in ccw_device_set_offline
If we detect a busy subchannel after the driver's set_offline
callback returned in ccw_device_set_offline, the current behavior
is to unregister the device, which may lead to undesired
consequences. Change this to just quiesce the subchannel and go on
with the offline processing.
Note: This is no excuse for not fixing these drivers -
after the set_offline callback they should have no running IO!
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | drivers/s390/cio/device.c | 29 |
1 files changed, 20 insertions, 9 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index dc97cb9f227f..9fecfb4223a8 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -314,6 +314,8 @@ static void ccw_device_unregister(struct ccw_device *cdev) } } +static void io_subchannel_quiesce(struct subchannel *); + /** * ccw_device_set_offline() - disable a ccw device for I/O * @cdev: target ccw device @@ -327,7 +329,8 @@ static void ccw_device_unregister(struct ccw_device *cdev) */ int ccw_device_set_offline(struct ccw_device *cdev) { - int ret; + struct subchannel *sch; + int ret, state; if (!cdev) return -ENODEV; @@ -341,6 +344,7 @@ int ccw_device_set_offline(struct ccw_device *cdev) } cdev->online = 0; spin_lock_irq(cdev->ccwlock); + sch = to_subchannel(cdev->dev.parent); /* Wait until a final state or DISCONNECTED is reached */ while (!dev_fsm_final_state(cdev) && cdev->private->state != DEV_STATE_DISCONNECTED) { @@ -349,9 +353,21 @@ int ccw_device_set_offline(struct ccw_device *cdev) cdev->private->state == DEV_STATE_DISCONNECTED)); spin_lock_irq(cdev->ccwlock); } - ret = ccw_device_offline(cdev); - if (ret) - goto error; + do { + ret = ccw_device_offline(cdev); + if (!ret) + break; + 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); + if (ret != -EBUSY) + goto error; + state = cdev->private->state; + spin_unlock_irq(cdev->ccwlock); + io_subchannel_quiesce(sch); + spin_lock_irq(cdev->ccwlock); + cdev->private->state = state; + } while (ret == -EBUSY); spin_unlock_irq(cdev->ccwlock); wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || cdev->private->state == DEV_STATE_DISCONNECTED)); @@ -368,9 +384,6 @@ int ccw_device_set_offline(struct ccw_device *cdev) 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); @@ -1059,8 +1072,6 @@ out_schedule: return 0; } -static void io_subchannel_quiesce(struct subchannel *); - static int io_subchannel_remove (struct subchannel *sch) { |