[S390] cio: trigger subchannel event at resume time

ccw_device_pm_restore: trigger subchannel event to better handle
changes to the subchannel device.

Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Sebastian Ott 2010-02-26 22:37:29 +01:00 committed by Martin Schwidefsky
parent 76e6fb4b86
commit 0d01bb8922
3 changed files with 65 additions and 53 deletions

View File

@ -1020,7 +1020,7 @@ static int css_settle(struct device_driver *drv, void *unused)
return 0;
}
static inline int css_complete_work(void)
int css_complete_work(void)
{
int ret;

View File

@ -146,6 +146,7 @@ extern struct channel_subsystem *channel_subsystems[];
/* Helper functions to build lists for the slow path. */
void css_schedule_eval(struct subchannel_id schid);
void css_schedule_eval_all(void);
int css_complete_work(void);
int sch_is_pseudo_sch(struct subchannel *);
struct schib;

View File

@ -1400,6 +1400,12 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
rc = 0;
goto out_unlock;
case IO_SCH_VERIFY:
if (cdev->private->flags.resuming == 1) {
if (cio_enable_subchannel(sch, (u32)(addr_t)sch)) {
ccw_device_set_notoper(cdev);
break;
}
}
/* Trigger path verification. */
io_subchannel_verify(sch);
rc = 0;
@ -1438,7 +1444,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
break;
case IO_SCH_UNREG_ATTACH:
/* Unregister ccw device. */
ccw_device_unregister(cdev);
if (!cdev->private->flags.resuming)
ccw_device_unregister(cdev);
break;
default:
break;
@ -1447,7 +1454,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
switch (action) {
case IO_SCH_ORPH_UNREG:
case IO_SCH_UNREG:
css_sch_device_unregister(sch);
if (!cdev || !cdev->private->flags.resuming)
css_sch_device_unregister(sch);
break;
case IO_SCH_ORPH_ATTACH:
case IO_SCH_UNREG_ATTACH:
@ -1769,20 +1777,36 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
if (cio_is_console(sch->schid))
goto out;
spin_lock_irq(sch->lock);
if (cio_is_console(sch->schid)) {
cio_enable_subchannel(sch, (u32)(addr_t)sch);
goto out_unlock;
}
/*
* While we were sleeping, devices may have gone or become
* available again. Kick re-detection.
*/
spin_lock_irq(sch->lock);
cdev->private->flags.resuming = 1;
css_schedule_eval(sch->schid);
spin_unlock_irq(sch->lock);
css_complete_work();
/* cdev may have been moved to a different subchannel. */
sch = to_subchannel(cdev->dev.parent);
spin_lock_irq(sch->lock);
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_OFFLINE)
goto out_unlock;
ccw_device_recognition(cdev);
spin_unlock_irq(sch->lock);
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) ||
cdev->private->state == DEV_STATE_DISCONNECTED);
out:
spin_lock_irq(sch->lock);
out_unlock:
cdev->private->flags.resuming = 0;
spin_unlock_irq(sch->lock);
}
static int resume_handle_boxed(struct ccw_device *cdev)
@ -1806,40 +1830,31 @@ static int resume_handle_disc(struct ccw_device *cdev)
static int ccw_device_pm_restore(struct device *dev)
{
struct ccw_device *cdev = to_ccwdev(dev);
struct subchannel *sch = to_subchannel(cdev->dev.parent);
int ret = 0, cm_enabled;
struct subchannel *sch;
int ret = 0;
__ccw_device_pm_restore(cdev);
sch = to_subchannel(cdev->dev.parent);
spin_lock_irq(sch->lock);
if (cio_is_console(sch->schid)) {
cio_enable_subchannel(sch, (u32)(addr_t)sch);
spin_unlock_irq(sch->lock);
if (cio_is_console(sch->schid))
goto out_restore;
}
cdev->private->flags.donotify = 0;
/* check recognition results */
switch (cdev->private->state) {
case DEV_STATE_OFFLINE:
case DEV_STATE_ONLINE:
cdev->private->flags.donotify = 0;
break;
case DEV_STATE_BOXED:
ret = resume_handle_boxed(cdev);
spin_unlock_irq(sch->lock);
if (ret)
goto out;
goto out_unlock;
goto out_restore;
case DEV_STATE_DISCONNECTED:
goto out_disc_unlock;
default:
goto out_unreg_unlock;
}
/* check if the device id has changed */
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
CIO_MSG_EVENT(0, "resume: sch 0.%x.%04x: failed (devno "
"changed from %04x to %04x)\n",
sch->schid.ssid, sch->schid.sch_no,
cdev->private->dev_id.devno,
sch->schib.pmcw.dev);
goto out_unreg_unlock;
ret = resume_handle_disc(cdev);
if (ret)
goto out_unlock;
goto out_restore;
}
/* check if the device type has changed */
if (!ccw_device_test_sense_data(cdev)) {
@ -1848,24 +1863,30 @@ static int ccw_device_pm_restore(struct device *dev)
ret = -ENODEV;
goto out_unlock;
}
if (!cdev->online) {
ret = 0;
if (!cdev->online)
goto out_unlock;
if (ccw_device_online(cdev)) {
ret = resume_handle_disc(cdev);
if (ret)
goto out_unlock;
goto out_restore;
}
spin_unlock_irq(sch->lock);
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
spin_lock_irq(sch->lock);
if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_BAD) {
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
ret = -ENODEV;
goto out_unlock;
}
ret = ccw_device_online(cdev);
if (ret)
goto out_disc_unlock;
cm_enabled = cdev->private->cmb != NULL;
spin_unlock_irq(sch->lock);
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
if (cdev->private->state != DEV_STATE_ONLINE) {
spin_lock_irq(sch->lock);
goto out_disc_unlock;
}
if (cm_enabled) {
/* reenable cmf, if needed */
if (cdev->private->cmb) {
spin_unlock_irq(sch->lock);
ret = ccw_set_cmf(cdev, 1);
spin_lock_irq(sch->lock);
if (ret) {
CIO_MSG_EVENT(2, "resume: cdev 0.%x.%04x: cmf failed "
"(rc=%d)\n", cdev->private->dev_id.ssid,
@ -1875,21 +1896,11 @@ static int ccw_device_pm_restore(struct device *dev)
}
out_restore:
spin_unlock_irq(sch->lock);
if (cdev->online && cdev->drv && cdev->drv->restore)
ret = cdev->drv->restore(cdev);
out:
return ret;
out_disc_unlock:
ret = resume_handle_disc(cdev);
spin_unlock_irq(sch->lock);
if (ret)
return ret;
goto out_restore;
out_unreg_unlock:
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
ret = -ENODEV;
out_unlock:
spin_unlock_irq(sch->lock);
return ret;