forked from luck/tmp_suning_uos_patched
[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:
parent
76e6fb4b86
commit
0d01bb8922
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user