Commit 56e6b796 authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky

[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: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 24a1872d
......@@ -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));
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);
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
spin_unlock_irq(sch->lock);
wait_event(cdev->private->wait_q,
cdev->private->state != DEV_STATE_QUIESCE);
spin_lock_irq(sch->lock);
}
cio_disable_subchannel(sch);
ret = cio_disable_subchannel(sch);
}
out_unlock:
spin_unlock_irq(sch->lock);
}
static int device_is_disconnected(struct ccw_device *cdev)
......
......@@ -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;
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);
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment