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 | |
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')
-rw-r--r-- | drivers/s390/cio/device.c | 35 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 17 |
2 files changed, 23 insertions, 29 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) |
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 7d42417bc2c7..862b89ef3ee6 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -911,10 +911,7 @@ static void | |||
911 | ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) | 911 | ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) |
912 | { | 912 | { |
913 | ccw_device_set_timeout(cdev, 0); | 913 | ccw_device_set_timeout(cdev, 0); |
914 | if (dev_event == DEV_EVENT_NOTOPER) | 914 | cdev->private->state = DEV_STATE_NOT_OPER; |
915 | cdev->private->state = DEV_STATE_NOT_OPER; | ||
916 | else | ||
917 | cdev->private->state = DEV_STATE_OFFLINE; | ||
918 | wake_up(&cdev->private->wait_q); | 915 | wake_up(&cdev->private->wait_q); |
919 | } | 916 | } |
920 | 917 | ||
@@ -924,17 +921,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event) | |||
924 | int ret; | 921 | int ret; |
925 | 922 | ||
926 | ret = ccw_device_cancel_halt_clear(cdev); | 923 | ret = ccw_device_cancel_halt_clear(cdev); |
927 | switch (ret) { | 924 | if (ret == -EBUSY) { |
928 | case 0: | 925 | ccw_device_set_timeout(cdev, HZ/10); |
929 | cdev->private->state = DEV_STATE_OFFLINE; | 926 | } else { |
930 | wake_up(&cdev->private->wait_q); | ||
931 | break; | ||
932 | case -ENODEV: | ||
933 | cdev->private->state = DEV_STATE_NOT_OPER; | 927 | cdev->private->state = DEV_STATE_NOT_OPER; |
934 | wake_up(&cdev->private->wait_q); | 928 | wake_up(&cdev->private->wait_q); |
935 | break; | ||
936 | default: | ||
937 | ccw_device_set_timeout(cdev, HZ/10); | ||
938 | } | 929 | } |
939 | } | 930 | } |
940 | 931 | ||