[S390] cio: trigger subchannel event at resume time
authorSebastian Ott <sebott@linux.vnet.ibm.com>
Fri, 26 Feb 2010 21:37:29 +0000 (22:37 +0100)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Fri, 26 Feb 2010 21:37:30 +0000 (22:37 +0100)
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>
drivers/s390/cio/css.c
drivers/s390/cio/css.h
drivers/s390/cio/device.c

index 169a277..2769da5 100644 (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;
 
index 325d813..7e37886 100644 (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;
index c7b2b7b..c6abb75 100644 (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;
-       }
-       ret = ccw_device_online(cdev);
-       if (ret)
-               goto out_disc_unlock;
 
-       cm_enabled = cdev->private->cmb != NULL;
+       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));
-       if (cdev->private->state != DEV_STATE_ONLINE) {
-               spin_lock_irq(sch->lock);
-               goto out_disc_unlock;
+       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;
        }
-       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;