[S390] cio: use sense-pgid operation for path verification
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Mon, 7 Dec 2009 11:51:31 +0000 (12:51 +0100)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Mon, 7 Dec 2009 11:51:31 +0000 (12:51 +0100)
Set-pgid operations fail for some device types under z/VM for which
the hypervisor has already set the pgid. Also reserved devices or
changed pgids are not correctly recognized. Fix these problems by
using a combination of sense-pgid and set-pgid and by also accepting
pre-defined pgid settings.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/device.h
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_pgid.c
drivers/s390/cio/io_sch.h

index 4e1775c..2df519b 100644 (file)
@@ -110,9 +110,6 @@ void ccw_device_sense_id_start(struct ccw_device *);
 void ccw_device_sense_id_done(struct ccw_device *, int);
 
 /* Function prototypes for path grouping stuff. */
-void ccw_device_sense_pgid_start(struct ccw_device *);
-void ccw_device_sense_pgid_done(struct ccw_device *, int);
-
 void ccw_device_verify_start(struct ccw_device *);
 void ccw_device_verify_done(struct ccw_device *, int);
 
index d6e315d..8d565ff 100644 (file)
@@ -394,33 +394,6 @@ ccw_device_done(struct ccw_device *cdev, int state)
        wake_up(&cdev->private->wait_q);
 }
 
-/*
- * Function called from device_pgid.c after sense path ground has completed.
- */
-void
-ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
-{
-       struct subchannel *sch;
-
-       sch = to_subchannel(cdev->dev.parent);
-       switch (err) {
-       case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
-       case 0: /* success */
-       case -EACCES: /* partial success, some paths not operational */
-               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);
-               return;
-       default:
-               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
-               return;
-       }
-       /* Start Path Group verification. */
-       cdev->private->state = DEV_STATE_VERIFY;
-       ccw_device_verify_start(cdev);
-}
-
 /*
  * Start device recognition.
  */
@@ -503,6 +476,7 @@ callback:
                }
                break;
        case -ETIME:
+       case -EUSERS:
                /* Reset oper notify indication after verify error. */
                cdev->private->flags.donotify = 0;
                ccw_device_done(cdev, DEV_STATE_BOXED);
@@ -540,16 +514,9 @@ ccw_device_online(struct ccw_device *cdev)
                        dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
                return ret;
        }
-       /* Do we want to do path grouping? */
-       if (!cdev->private->options.pgroup) {
-               /* Start initial path verification. */
-               cdev->private->state = DEV_STATE_VERIFY;
-               ccw_device_verify_start(cdev);
-               return 0;
-       }
-       /* Do a SensePGID first. */
-       cdev->private->state = DEV_STATE_SENSE_PGID;
-       ccw_device_sense_pgid_start(cdev);
+       /* Start initial path verification. */
+       cdev->private->state = DEV_STATE_VERIFY;
+       ccw_device_verify_start(cdev);
        return 0;
 }
 
index 3323042..4d54abd 100644 (file)
@@ -141,8 +141,8 @@ static void spid_do(struct ccw_device *cdev)
        struct ccw_request *req = &cdev->private->req;
        u8 fn;
 
-       /* Adjust lpm if paths are not set in pam. */
-       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam);
+       /* Use next available path that is not already in correct state. */
+       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm);
        if (!req->lpm)
                goto out_nopath;
        /* Channel program setup. */
@@ -199,6 +199,19 @@ err:
        verify_done(cdev, rc);
 }
 
+static void spid_start(struct ccw_device *cdev)
+{
+       struct ccw_request *req = &cdev->private->req;
+
+       /* Initialize request data. */
+       memset(req, 0, sizeof(*req));
+       req->timeout    = PGID_TIMEOUT;
+       req->maxretries = PGID_RETRIES;
+       req->lpm        = 0x80;
+       req->callback   = spid_callback;
+       spid_do(cdev);
+}
+
 static int pgid_cmp(struct pgid *p1, struct pgid *p2)
 {
        return memcmp((char *) p1 + 1, (char *) p2 + 1,
@@ -241,6 +254,40 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
        *p = first;
 }
 
+static u8 pgid_to_vpm(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct pgid *pgid;
+       int i;
+       int lpm;
+       u8 vpm = 0;
+
+       /* Set VPM bits for paths which are already in the target state. */
+       for (i = 0; i < 8; i++) {
+               lpm = 0x80 >> i;
+               if ((cdev->private->pgid_valid_mask & lpm) == 0)
+                       continue;
+               pgid = &cdev->private->pgid[i];
+               if (sch->opm & lpm) {
+                       if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED)
+                               continue;
+               } else {
+                       if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED)
+                               continue;
+               }
+               if (cdev->private->flags.mpath) {
+                       if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH)
+                               continue;
+               } else {
+                       if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH)
+                               continue;
+               }
+               vpm |= lpm;
+       }
+
+       return vpm;
+}
+
 static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid)
 {
        int i;
@@ -255,6 +302,7 @@ static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid)
 static void snid_done(struct ccw_device *cdev, int rc)
 {
        struct ccw_dev_id *id = &cdev->private->dev_id;
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
        struct pgid *pgid;
        int mismatch = 0;
        int reserved = 0;
@@ -263,18 +311,38 @@ static void snid_done(struct ccw_device *cdev, int rc)
        if (rc)
                goto out;
        pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset);
-       if (!mismatch) {
-               pgid_fill(cdev, pgid);
-               cdev->private->flags.pgid_rdy = 1;
-       }
        if (reserved)
                rc = -EUSERS;
+       else if (mismatch)
+               rc = -EOPNOTSUPP;
+       else {
+               sch->vpm = pgid_to_vpm(cdev);
+               pgid_fill(cdev, pgid);
+       }
 out:
-       CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x mism=%d "
-                     "rsvd=%d reset=%d\n", id->ssid, id->devno, rc,
-                     cdev->private->pgid_valid_mask, mismatch, reserved,
-                     reset);
-       ccw_device_sense_pgid_done(cdev, rc);
+       CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
+                     "mism=%d rsvd=%d reset=%d\n", id->ssid, id->devno, rc,
+                     cdev->private->pgid_valid_mask, sch->vpm, mismatch,
+                     reserved, reset);
+       switch (rc) {
+       case 0:
+               /* Anything left to do? */
+               if (sch->vpm == sch->schib.pmcw.pam) {
+                       verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
+                       return;
+               }
+               /* Perform path-grouping. */
+               spid_start(cdev);
+               break;
+       case -EOPNOTSUPP:
+               /* Path-grouping not supported. */
+               cdev->private->flags.pgroup = 0;
+               cdev->private->flags.mpath = 0;
+               verify_start(cdev);
+               break;
+       default:
+               verify_done(cdev, rc);
+       }
 }
 
 /*
@@ -333,33 +401,6 @@ err:
        snid_done(cdev, rc);
 }
 
-/**
- * ccw_device_sense_pgid_start - perform SENSE PGID
- * @cdev: ccw device
- *
- * Execute a SENSE PGID channel program on each path to @cdev to update its
- * PGID information. When finished, call ccw_device_sense_id_done with a
- * return code specifying the result.
- */
-void ccw_device_sense_pgid_start(struct ccw_device *cdev)
-{
-       struct ccw_request *req = &cdev->private->req;
-
-       CIO_TRACE_EVENT(4, "snid");
-       CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
-       /* Initialize PGID data. */
-       memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid));
-       cdev->private->flags.pgid_rdy = 0;
-       cdev->private->pgid_valid_mask = 0;
-       /* Initialize request data. */
-       memset(req, 0, sizeof(*req));
-       req->timeout    = PGID_TIMEOUT;
-       req->maxretries = PGID_RETRIES;
-       req->callback   = snid_callback;
-       req->lpm        = 0x80;
-       snid_do(cdev);
-}
-
 /*
  * Perform path verification.
  */
@@ -367,6 +408,7 @@ static void verify_start(struct ccw_device *cdev)
 {
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
        struct ccw_request *req = &cdev->private->req;
+       struct ccw_dev_id *devid = &cdev->private->dev_id;
 
        sch->vpm = 0;
        /* Initialize request data. */
@@ -375,9 +417,13 @@ static void verify_start(struct ccw_device *cdev)
        req->maxretries = PGID_RETRIES;
        req->lpm        = 0x80;
        if (cdev->private->flags.pgroup) {
-               req->callback   = spid_callback;
-               spid_do(cdev);
+               CIO_TRACE_EVENT(4, "snid");
+               CIO_HEX_EVENT(4, devid, sizeof(*devid));
+               req->callback   = snid_callback;
+               snid_do(cdev);
        } else {
+               CIO_TRACE_EVENT(4, "nop");
+               CIO_HEX_EVENT(4, devid, sizeof(*devid));
                req->filter     = nop_filter;
                req->callback   = nop_callback;
                nop_do(cdev);
@@ -398,19 +444,15 @@ void ccw_device_verify_start(struct ccw_device *cdev)
 {
        CIO_TRACE_EVENT(4, "vrfy");
        CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
-       if (!cdev->private->flags.pgid_rdy) {
-               /* No pathgrouping possible. */
-               cdev->private->flags.pgroup = 0;
-               cdev->private->flags.mpath = 0;
-       } else {
-               /*
-                * Initialize pathgroup and multipath state with target values.
-                * They may change in the course of path verification.
-                */
-               cdev->private->flags.pgroup = cdev->private->options.pgroup;
-               cdev->private->flags.mpath = cdev->private->options.mpath;
-
-       }
+       /* Initialize PGID data. */
+       memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid));
+       cdev->private->pgid_valid_mask = 0;
+       /*
+        * Initialize pathgroup and multipath state with target values.
+        * They may change in the course of path verification.
+        */
+       cdev->private->flags.pgroup = cdev->private->options.pgroup;
+       cdev->private->flags.mpath = cdev->private->options.mpath;
        cdev->private->flags.doverify = 0;
        verify_start(cdev);
 }
index b387c80..0559479 100644 (file)
@@ -166,7 +166,6 @@ struct ccw_device_private {
                unsigned int recog_done:1;  /* dev. recog. complete */
                unsigned int fake_irb:1;    /* deliver faked irb */
                unsigned int resuming:1;    /* recognition while resume */
-               unsigned int pgid_rdy:1;    /* pgids are ready */
                unsigned int pgroup:1;      /* pathgroup is set up */
                unsigned int mpath:1;       /* multipathing is set up */
        } __attribute__((packed)) flags;