diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-12-07 06:51:35 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 06:51:32 -0500 |
commit | 56e6b796fe9b99287648fc5686aae00106b37bab (patch) | |
tree | f0c75986905f3ff01ec1c801843f94923509af06 /drivers/s390/cio/device.c | |
parent | 24a1872d6411c7cce82c0888a4fbea23e993e051 (diff) |
[S390] cio: fix quiesce state
DEV_STATE_QUIESCE is used to stop all IO on a busy subchannel.
This patch fixes the following problems related to the QUIESCE
state:
* Fix a potential race condition which could occur when the
resulting state was DEV_STATE_OFFLINE.
* Add missing locking around cio_disable_subchannel,
ccw_device_cancel_halt_clear and the cdev's handler.
* Loop until we know for sure that the subchannel is disabled.
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 35 |
1 files changed, 19 insertions, 16 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e8cb99a63cc6..2b50f93b7fef 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -1130,33 +1130,36 @@ static int io_subchannel_chp_event(struct subchannel *sch, | |||
1130 | return 0; | 1130 | return 0; |
1131 | } | 1131 | } |
1132 | 1132 | ||
1133 | static void | 1133 | static void io_subchannel_shutdown(struct subchannel *sch) |
1134 | io_subchannel_shutdown(struct subchannel *sch) | ||
1135 | { | 1134 | { |
1136 | struct ccw_device *cdev; | 1135 | struct ccw_device *cdev; |
1137 | int ret; | 1136 | int ret; |
1138 | 1137 | ||
1138 | spin_lock_irq(sch->lock); | ||
1139 | cdev = sch_get_cdev(sch); | 1139 | cdev = sch_get_cdev(sch); |
1140 | |||
1141 | if (cio_is_console(sch->schid)) | 1140 | if (cio_is_console(sch->schid)) |
1142 | return; | 1141 | goto out_unlock; |
1143 | if (!sch->schib.pmcw.ena) | 1142 | if (!sch->schib.pmcw.ena) |
1144 | /* Nothing to do. */ | 1143 | goto out_unlock; |
1145 | return; | ||
1146 | ret = cio_disable_subchannel(sch); | 1144 | ret = cio_disable_subchannel(sch); |
1147 | if (ret != -EBUSY) | 1145 | if (ret != -EBUSY) |
1148 | /* Subchannel is disabled, we're done. */ | 1146 | goto out_unlock; |
1149 | return; | ||
1150 | cdev->private->state = DEV_STATE_QUIESCE; | ||
1151 | if (cdev->handler) | 1147 | if (cdev->handler) |
1152 | cdev->handler(cdev, cdev->private->intparm, | 1148 | cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); |
1153 | ERR_PTR(-EIO)); | 1149 | while (ret == -EBUSY) { |
1154 | ret = ccw_device_cancel_halt_clear(cdev); | 1150 | cdev->private->state = DEV_STATE_QUIESCE; |
1155 | if (ret == -EBUSY) { | 1151 | ret = ccw_device_cancel_halt_clear(cdev); |
1156 | ccw_device_set_timeout(cdev, HZ/10); | 1152 | if (ret == -EBUSY) { |
1157 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); | 1153 | ccw_device_set_timeout(cdev, HZ/10); |
1154 | spin_unlock_irq(sch->lock); | ||
1155 | wait_event(cdev->private->wait_q, | ||
1156 | cdev->private->state != DEV_STATE_QUIESCE); | ||
1157 | spin_lock_irq(sch->lock); | ||
1158 | } | ||
1159 | ret = cio_disable_subchannel(sch); | ||
1158 | } | 1160 | } |
1159 | cio_disable_subchannel(sch); | 1161 | out_unlock: |
1162 | spin_unlock_irq(sch->lock); | ||
1160 | } | 1163 | } |
1161 | 1164 | ||
1162 | static int device_is_disconnected(struct ccw_device *cdev) | 1165 | static int device_is_disconnected(struct ccw_device *cdev) |