aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2009-12-07 06:51:35 -0500
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2009-12-07 06:51:32 -0500
commit56e6b796fe9b99287648fc5686aae00106b37bab (patch)
treef0c75986905f3ff01ec1c801843f94923509af06 /drivers/s390/cio
parent24a1872d6411c7cce82c0888a4fbea23e993e051 (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.c35
-rw-r--r--drivers/s390/cio/device_fsm.c17
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
1133static void 1133static void io_subchannel_shutdown(struct subchannel *sch)
1134io_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); 1161out_unlock:
1162 spin_unlock_irq(sch->lock);
1160} 1163}
1161 1164
1162static int device_is_disconnected(struct ccw_device *cdev) 1165static 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
911ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) 911ccw_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