diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 169a27723c4f..2769da54f2b9 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -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; diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 325d813bb8e0..7e37886de231 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -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; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index c7b2b7b26b8b..c6abb75c4615 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -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;