[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; return 0;
} }
static inline int css_complete_work(void) int css_complete_work(void)
{ {
int ret; int ret;

View File

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

View File

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