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, ...@@ -1130,33 +1130,36 @@ static int io_subchannel_chp_event(struct subchannel *sch,
return 0; return 0;
} }
static void static void io_subchannel_shutdown(struct subchannel *sch)
io_subchannel_shutdown(struct subchannel *sch)
{ {
struct ccw_device *cdev; struct ccw_device *cdev;
int ret; int ret;
spin_lock_irq(sch->lock);
cdev = sch_get_cdev(sch); cdev = sch_get_cdev(sch);
if (cio_is_console(sch->schid)) if (cio_is_console(sch->schid))
return; goto out_unlock;
if (!sch->schib.pmcw.ena) if (!sch->schib.pmcw.ena)
/* Nothing to do. */ goto out_unlock;
return;
ret = cio_disable_subchannel(sch); ret = cio_disable_subchannel(sch);
if (ret != -EBUSY) if (ret != -EBUSY)
/* Subchannel is disabled, we're done. */ goto out_unlock;
return;
cdev->private->state = DEV_STATE_QUIESCE;
if (cdev->handler) if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm, cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
ERR_PTR(-EIO)); while (ret == -EBUSY) {
ret = ccw_device_cancel_halt_clear(cdev); cdev->private->state = DEV_STATE_QUIESCE;
if (ret == -EBUSY) { ret = ccw_device_cancel_halt_clear(cdev);
ccw_device_set_timeout(cdev, HZ/10); if (ret == -EBUSY) {
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); 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) static int device_is_disconnected(struct ccw_device *cdev)
......
...@@ -911,10 +911,7 @@ static void ...@@ -911,10 +911,7 @@ static void
ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
{ {
ccw_device_set_timeout(cdev, 0); ccw_device_set_timeout(cdev, 0);
if (dev_event == DEV_EVENT_NOTOPER) cdev->private->state = DEV_STATE_NOT_OPER;
cdev->private->state = DEV_STATE_NOT_OPER;
else
cdev->private->state = DEV_STATE_OFFLINE;
wake_up(&cdev->private->wait_q); wake_up(&cdev->private->wait_q);
} }
...@@ -924,17 +921,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -924,17 +921,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event)
int ret; int ret;
ret = ccw_device_cancel_halt_clear(cdev); ret = ccw_device_cancel_halt_clear(cdev);
switch (ret) { if (ret == -EBUSY) {
case 0: ccw_device_set_timeout(cdev, HZ/10);
cdev->private->state = DEV_STATE_OFFLINE; } else {
wake_up(&cdev->private->wait_q);
break;
case -ENODEV:
cdev->private->state = DEV_STATE_NOT_OPER; cdev->private->state = DEV_STATE_NOT_OPER;
wake_up(&cdev->private->wait_q); 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