[S390] Some preparations for the dynamic subchannel mapping patch.
[firefly-linux-kernel-4.4.55.git] / drivers / s390 / cio / device_fsm.c
index fcaf28d7b4eb6486f2addf5b462d8551bffb2072..a487fb0e7d3d5c2d79453cc987f04d05ed960553 100644 (file)
@@ -59,6 +59,27 @@ device_set_disconnected(struct subchannel *sch)
        cdev->private->state = DEV_STATE_DISCONNECTED;
 }
 
+void device_set_intretry(struct subchannel *sch)
+{
+       struct ccw_device *cdev;
+
+       cdev = sch->dev.driver_data;
+       if (!cdev)
+               return;
+       cdev->private->flags.intretry = 1;
+}
+
+int device_trigger_verify(struct subchannel *sch)
+{
+       struct ccw_device *cdev;
+
+       cdev = sch->dev.driver_data;
+       if (!cdev || !cdev->online)
+               return -EINVAL;
+       dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+       return 0;
+}
+
 /*
  * Timeout function. It just triggers a DEV_EVENT_TIMEOUT.
  */
@@ -173,7 +194,7 @@ ccw_device_handle_oper(struct ccw_device *cdev)
            cdev->id.dev_model != cdev->private->senseid.dev_model ||
            cdev->private->dev_id.devno != sch->schib.pmcw.dev) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_do_unreg_rereg, cdev);
+                            ccw_device_do_unreg_rereg);
                queue_work(ccw_device_work, &cdev->private->kick_work);
                return 0;
        }
@@ -308,19 +329,21 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err)
 }
 
 static void
-ccw_device_oper_notify(void *data)
+ccw_device_oper_notify(struct work_struct *work)
 {
+       struct ccw_device_private *priv;
        struct ccw_device *cdev;
        struct subchannel *sch;
        int ret;
 
-       cdev = data;
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
        ret = (sch->driver && sch->driver->notify) ?
                sch->driver->notify(&sch->dev, CIO_OPER) : 0;
        if (!ret)
                /* Driver doesn't want device back. */
-               ccw_device_do_unreg_rereg(cdev);
+               ccw_device_do_unreg_rereg(work);
        else {
                /* Reenable channel measurements, if needed. */
                cmf_reenable(cdev);
@@ -356,8 +379,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
 
        if (cdev->private->flags.donotify) {
                cdev->private->flags.donotify = 0;
-               PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify,
-                            cdev);
+               PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify);
                queue_work(ccw_device_notify_work, &cdev->private->kick_work);
        }
        wake_up(&cdev->private->wait_q);
@@ -507,13 +529,15 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event)
 
 
 static void
-ccw_device_nopath_notify(void *data)
+ccw_device_nopath_notify(struct work_struct *work)
 {
+       struct ccw_device_private *priv;
        struct ccw_device *cdev;
        struct subchannel *sch;
        int ret;
 
-       cdev = data;
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
        /* Extra sanity. */
        if (sch->lpm)
@@ -526,8 +550,7 @@ ccw_device_nopath_notify(void *data)
                        cio_disable_subchannel(sch);
                        if (get_device(&cdev->dev)) {
                                PREPARE_WORK(&cdev->private->kick_work,
-                                            ccw_device_call_sch_unregister,
-                                            cdev);
+                                            ccw_device_call_sch_unregister);
                                queue_work(ccw_device_work,
                                           &cdev->private->kick_work);
                        } else
@@ -578,11 +601,15 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
                }
                break;
        case -ETIME:
+               /* Reset oper notify indication after verify error. */
+               cdev->private->flags.donotify = 0;
                ccw_device_done(cdev, DEV_STATE_BOXED);
                break;
        default:
+               /* Reset oper notify indication after verify error. */
+               cdev->private->flags.donotify = 0;
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_nopath_notify, cdev);
+                            ccw_device_nopath_notify);
                queue_work(ccw_device_notify_work, &cdev->private->kick_work);
                ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
@@ -713,7 +740,7 @@ ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event)
        sch = to_subchannel(cdev->dev.parent);
        if (get_device(&cdev->dev)) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_call_sch_unregister, cdev);
+                            ccw_device_call_sch_unregister);
                queue_work(ccw_device_work, &cdev->private->kick_work);
        }
        wake_up(&cdev->private->wait_q);
@@ -744,7 +771,7 @@ ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event)
        }
        if (get_device(&cdev->dev)) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_call_sch_unregister, cdev);
+                            ccw_device_call_sch_unregister);
                queue_work(ccw_device_work, &cdev->private->kick_work);
        }
        wake_up(&cdev->private->wait_q);
@@ -849,7 +876,7 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
                sch = to_subchannel(cdev->dev.parent);
                if (!sch->lpm) {
                        PREPARE_WORK(&cdev->private->kick_work,
-                                    ccw_device_nopath_notify, cdev);
+                                    ccw_device_nopath_notify);
                        queue_work(ccw_device_notify_work,
                                   &cdev->private->kick_work);
                } else
@@ -889,6 +916,12 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
         * had killed the original request.
         */
        if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
+               /* Retry Basic Sense if requested. */
+               if (cdev->private->flags.intretry) {
+                       cdev->private->flags.intretry = 0;
+                       ccw_device_do_sense(cdev, irb);
+                       return;
+               }
                cdev->private->flags.dosense = 0;
                memset(&cdev->private->irb, 0, sizeof(struct irb));
                ccw_device_accumulate_irb(cdev, irb);
@@ -938,7 +971,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
                              ERR_PTR(-EIO));
        if (!sch->lpm) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_nopath_notify, cdev);
+                            ccw_device_nopath_notify);
                queue_work(ccw_device_notify_work, &cdev->private->kick_work);
        } else if (cdev->private->flags.doverify)
                /* Start delayed path verification. */
@@ -961,7 +994,7 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
                sch = to_subchannel(cdev->dev.parent);
                if (!sch->lpm) {
                        PREPARE_WORK(&cdev->private->kick_work,
-                                    ccw_device_nopath_notify, cdev);
+                                    ccw_device_nopath_notify);
                        queue_work(ccw_device_notify_work,
                                   &cdev->private->kick_work);
                } else
@@ -990,7 +1023,7 @@ void device_kill_io(struct subchannel *sch)
        if (ret == -ENODEV) {
                if (!sch->lpm) {
                        PREPARE_WORK(&cdev->private->kick_work,
-                                    ccw_device_nopath_notify, cdev);
+                                    ccw_device_nopath_notify);
                        queue_work(ccw_device_notify_work,
                                   &cdev->private->kick_work);
                } else
@@ -1002,7 +1035,7 @@ void device_kill_io(struct subchannel *sch)
                              ERR_PTR(-EIO));
        if (!sch->lpm) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_nopath_notify, cdev);
+                            ccw_device_nopath_notify);
                queue_work(ccw_device_notify_work, &cdev->private->kick_work);
        } else
                /* Start delayed path verification. */
@@ -1073,7 +1106,8 @@ device_trigger_reprobe(struct subchannel *sch)
        /* Update some values. */
        if (stsch(sch->schid, &sch->schib))
                return;
-
+       if (!sch->schib.pmcw.dnv)
+               return;
        /*
         * The pim, pam, pom values may not be accurate, but they are the best
         * we have before performing device selection :/