[S390] path grouping and path verifications fixes.
[firefly-linux-kernel-4.4.55.git] / drivers / s390 / cio / device_fsm.c
index cb1af0b6f033c4b43e057f29e9ddf2ef2d4596b0..ac6e0c7e43d9587805e820e8d2e6eb633989d58d 100644 (file)
@@ -378,6 +378,56 @@ ccw_device_done(struct ccw_device *cdev, int state)
                put_device (&cdev->dev);
 }
 
+static inline int cmp_pgid(struct pgid *p1, struct pgid *p2)
+{
+       char *c1;
+       char *c2;
+
+       c1 = (char *)p1;
+       c2 = (char *)p2;
+
+       return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1);
+}
+
+static void __ccw_device_get_common_pgid(struct ccw_device *cdev)
+{
+       int i;
+       int last;
+
+       last = 0;
+       for (i = 0; i < 8; i++) {
+               if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET)
+                       /* No PGID yet */
+                       continue;
+               if (cdev->private->pgid[last].inf.ps.state1 ==
+                   SNID_STATE1_RESET) {
+                       /* First non-zero PGID */
+                       last = i;
+                       continue;
+               }
+               if (cmp_pgid(&cdev->private->pgid[i],
+                            &cdev->private->pgid[last]) == 0)
+                       /* Non-conflicting PGIDs */
+                       continue;
+
+               /* PGID mismatch, can't pathgroup. */
+               CIO_MSG_EVENT(0, "SNID - pgid mismatch for device "
+                             "0.%x.%04x, can't pathgroup\n",
+                             cdev->private->ssid, cdev->private->devno);
+               cdev->private->options.pgroup = 0;
+               return;
+       }
+       if (cdev->private->pgid[last].inf.ps.state1 ==
+           SNID_STATE1_RESET)
+               /* No previous pgid found */
+               memcpy(&cdev->private->pgid[0], &css[0]->global_pgid,
+                      sizeof(struct pgid));
+       else
+               /* Use existing pgid */
+               memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last],
+                      sizeof(struct pgid));
+}
+
 /*
  * Function called from device_pgid.c after sense path ground has completed.
  */
@@ -388,24 +438,26 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
 
        sch = to_subchannel(cdev->dev.parent);
        switch (err) {
-       case 0:
-               /* Start Path Group verification. */
-               sch->vpm = 0;   /* Start with no path groups set. */
-               cdev->private->state = DEV_STATE_VERIFY;
-               ccw_device_verify_start(cdev);
+       case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
+               cdev->private->options.pgroup = 0;
+               break;
+       case 0: /* success */
+       case -EACCES: /* partial success, some paths not operational */
+               /* Check if all pgids are equal or 0. */
+               __ccw_device_get_common_pgid(cdev);
                break;
        case -ETIME:            /* Sense path group id stopped by timeout. */
        case -EUSERS:           /* device is reserved for someone else. */
                ccw_device_done(cdev, DEV_STATE_BOXED);
-               break;
-       case -EOPNOTSUPP: /* path grouping not supported, just set online. */
-               cdev->private->options.pgroup = 0;
-               ccw_device_done(cdev, DEV_STATE_ONLINE);
-               break;
+               return;
        default:
                ccw_device_done(cdev, DEV_STATE_NOT_OPER);
-               break;
+               return;
        }
+       /* Start Path Group verification. */
+       sch->vpm = 0;   /* Start with no path groups set. */
+       cdev->private->state = DEV_STATE_VERIFY;
+       ccw_device_verify_start(cdev);
 }
 
 /*
@@ -562,8 +614,9 @@ ccw_device_online(struct ccw_device *cdev)
        }
        /* Do we want to do path grouping? */
        if (!cdev->private->options.pgroup) {
-               /* No, set state online immediately. */
-               ccw_device_done(cdev, DEV_STATE_ONLINE);
+               /* Start initial path verification. */
+               cdev->private->state = DEV_STATE_VERIFY;
+               ccw_device_verify_start(cdev);
                return 0;
        }
        /* Do a SensePGID first. */
@@ -609,6 +662,7 @@ ccw_device_offline(struct ccw_device *cdev)
        /* Are we doing path grouping? */
        if (!cdev->private->options.pgroup) {
                /* No, set state offline immediately. */
+               sch->vpm = 0;
                ccw_device_done(cdev, DEV_STATE_OFFLINE);
                return 0;
        }
@@ -705,8 +759,6 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
 {
        struct subchannel *sch;
 
-       if (!cdev->private->options.pgroup)
-               return;
        if (cdev->private->state == DEV_STATE_W4SENSE) {
                cdev->private->flags.doverify = 1;
                return;
@@ -995,8 +1047,7 @@ static void
 ccw_device_wait4io_verify(struct ccw_device *cdev, enum dev_event dev_event)
 {
        /* When the I/O has terminated, we have to start verification. */
-       if (cdev->private->options.pgroup)
-               cdev->private->flags.doverify = 1;
+       cdev->private->flags.doverify = 1;
 }
 
 static void