diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-12-07 11:51:35 (GMT) |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 11:51:32 (GMT) |
commit | 56e6b796fe9b99287648fc5686aae00106b37bab (patch) | |
tree | f0c75986905f3ff01ec1c801843f94923509af06 | |
parent | 24a1872d6411c7cce82c0888a4fbea23e993e051 (diff) | |
download | linux-56e6b796fe9b99287648fc5686aae00106b37bab.tar.xz |
[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>
-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 e8cb99a..2b50f93 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, return 0; } -static void -io_subchannel_shutdown(struct subchannel *sch) +static void io_subchannel_shutdown(struct subchannel *sch) { struct ccw_device *cdev; int ret; + spin_lock_irq(sch->lock); cdev = sch_get_cdev(sch); - if (cio_is_console(sch->schid)) - return; + goto out_unlock; if (!sch->schib.pmcw.ena) - /* Nothing to do. */ - return; + goto out_unlock; ret = cio_disable_subchannel(sch); if (ret != -EBUSY) - /* Subchannel is disabled, we're done. */ - return; - cdev->private->state = DEV_STATE_QUIESCE; + goto out_unlock; if (cdev->handler) - cdev->handler(cdev, cdev->private->intparm, - ERR_PTR(-EIO)); - ret = ccw_device_cancel_halt_clear(cdev); - if (ret == -EBUSY) { - ccw_device_set_timeout(cdev, HZ/10); - wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); + cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); + while (ret == -EBUSY) { + cdev->private->state = DEV_STATE_QUIESCE; + ret = ccw_device_cancel_halt_clear(cdev); + if (ret == -EBUSY) { + ccw_device_set_timeout(cdev, HZ/10); + spin_unlock_irq(sch->lock); + wait_event(cdev->private->wait_q, + cdev->private->state != DEV_STATE_QUIESCE); + spin_lock_irq(sch->lock); + } + ret = cio_disable_subchannel(sch); } - cio_disable_subchannel(sch); +out_unlock: + spin_unlock_irq(sch->lock); } 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 7d42417..862b89e 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -911,10 +911,7 @@ static void ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) { ccw_device_set_timeout(cdev, 0); - if (dev_event == DEV_EVENT_NOTOPER) - cdev->private->state = DEV_STATE_NOT_OPER; - else - cdev->private->state = DEV_STATE_OFFLINE; + cdev->private->state = DEV_STATE_NOT_OPER; wake_up(&cdev->private->wait_q); } @@ -924,17 +921,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event) int ret; ret = ccw_device_cancel_halt_clear(cdev); - switch (ret) { - case 0: - cdev->private->state = DEV_STATE_OFFLINE; - wake_up(&cdev->private->wait_q); - break; - case -ENODEV: + if (ret == -EBUSY) { + ccw_device_set_timeout(cdev, HZ/10); + } else { cdev->private->state = DEV_STATE_NOT_OPER; wake_up(&cdev->private->wait_q); - break; - default: - ccw_device_set_timeout(cdev, HZ/10); } } |