Commit 0c609fca authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky

[S390] cio: handle busy subchannel in ccw_device_move_to_sch

Try to disable the old subchannel before we ask the driver core
to move the attached device to a new parent. This way we can use
the QUIESCE state during shutdown which prevents a possible use
after free situation in some error cases.
Signed-off-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent ec64333c
...@@ -892,12 +892,27 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev, ...@@ -892,12 +892,27 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
struct subchannel *sch) struct subchannel *sch)
{ {
struct subchannel *old_sch; struct subchannel *old_sch;
int rc; int rc, old_enabled = 0;
old_sch = to_subchannel(cdev->dev.parent); old_sch = to_subchannel(cdev->dev.parent);
/* Obtain child reference for new parent. */ /* Obtain child reference for new parent. */
if (!get_device(&sch->dev)) if (!get_device(&sch->dev))
return -ENODEV; return -ENODEV;
if (!sch_is_pseudo_sch(old_sch)) {
spin_lock_irq(old_sch->lock);
old_enabled = old_sch->schib.pmcw.ena;
rc = 0;
if (old_enabled)
rc = cio_disable_subchannel(old_sch);
spin_unlock_irq(old_sch->lock);
if (rc == -EBUSY) {
/* Release child reference for new parent. */
put_device(&sch->dev);
return rc;
}
}
mutex_lock(&sch->reg_mutex); mutex_lock(&sch->reg_mutex);
rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
mutex_unlock(&sch->reg_mutex); mutex_unlock(&sch->reg_mutex);
...@@ -906,6 +921,12 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev, ...@@ -906,6 +921,12 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
cdev->private->dev_id.ssid, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, sch->schid.ssid, cdev->private->dev_id.devno, sch->schid.ssid,
sch->schib.pmcw.dev, rc); sch->schib.pmcw.dev, rc);
if (old_enabled) {
/* Try to reenable the old subchannel. */
spin_lock_irq(old_sch->lock);
cio_enable_subchannel(old_sch, (u32)(addr_t)old_sch);
spin_unlock_irq(old_sch->lock);
}
/* Release child reference for new parent. */ /* Release child reference for new parent. */
put_device(&sch->dev); put_device(&sch->dev);
return rc; return rc;
...@@ -914,7 +935,6 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev, ...@@ -914,7 +935,6 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
if (!sch_is_pseudo_sch(old_sch)) { if (!sch_is_pseudo_sch(old_sch)) {
spin_lock_irq(old_sch->lock); spin_lock_irq(old_sch->lock);
sch_set_cdev(old_sch, NULL); sch_set_cdev(old_sch, NULL);
cio_disable_subchannel(old_sch);
spin_unlock_irq(old_sch->lock); spin_unlock_irq(old_sch->lock);
css_schedule_eval(old_sch->schid); css_schedule_eval(old_sch->schid);
} }
......
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