[S390] cio: fix channel path vary
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Fri, 18 Dec 2009 16:43:23 +0000 (17:43 +0100)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Fri, 18 Dec 2009 16:43:32 +0000 (17:43 +0100)
Channel path vary is currently broken: channel paths which are varied
offline are still used by Linux. The reason for this is that:

 * the path mask indicating which paths of an I/O device can be used
   is reset by each internal I/O request
 * the logic that checks if a path group is already in its designated
   target state incorrectly interprets the result "is correctly set"
   as "is correctly set and available"

Fix this by resetting the path mask only for internal I/O requests
which affect the path mask and by correcting the pgid check logic.

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

index 9509e38..7a28a30 100644 (file)
@@ -49,7 +49,6 @@ static u16 ccwreq_next_path(struct ccw_device *cdev)
  */
 static void ccwreq_stop(struct ccw_device *cdev, int rc)
 {
-       struct subchannel *sch = to_subchannel(cdev->dev.parent);
        struct ccw_request *req = &cdev->private->req;
 
        if (req->done)
@@ -57,7 +56,6 @@ static void ccwreq_stop(struct ccw_device *cdev, int rc)
        req->done = 1;
        ccw_device_set_timeout(cdev, 0);
        memset(&cdev->private->irb, 0, sizeof(struct irb));
-       sch->lpm = sch->schib.pmcw.pam;
        if (rc && rc != -ENODEV && req->drc)
                rc = req->drc;
        req->callback(cdev, req->data, rc);
@@ -80,7 +78,6 @@ static void ccwreq_do(struct ccw_device *cdev)
                        continue;
                }
                /* Perform start function. */
-               sch->lpm = 0xff;
                memset(&cdev->private->irb, 0, sizeof(struct irb));
                rc = cio_start(sch, cp, (u8) req->mask);
                if (rc == 0) {
index aad188e..6facb54 100644 (file)
@@ -142,7 +142,7 @@ static void spid_do(struct ccw_device *cdev)
        u8 fn;
 
        /* Use next available path that is not already in correct state. */
-       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm);
+       req->lpm = lpm_adjust(req->lpm, cdev->private->pgid_todo_mask);
        if (!req->lpm)
                goto out_nopath;
        /* Channel program setup. */
@@ -254,15 +254,15 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
        *p = first;
 }
 
-static u8 pgid_to_vpm(struct ccw_device *cdev)
+static u8 pgid_to_donepm(struct ccw_device *cdev)
 {
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
        struct pgid *pgid;
        int i;
        int lpm;
-       u8 vpm = 0;
+       u8 donepm = 0;
 
-       /* Set VPM bits for paths which are already in the target state. */
+       /* Set 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)
@@ -282,10 +282,10 @@ static u8 pgid_to_vpm(struct ccw_device *cdev)
                        if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH)
                                continue;
                }
-               vpm |= lpm;
+               donepm |= lpm;
        }
 
-       return vpm;
+       return donepm;
 }
 
 static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid)
@@ -307,6 +307,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
        int mismatch = 0;
        int reserved = 0;
        int reset = 0;
+       u8 donepm;
 
        if (rc)
                goto out;
@@ -316,18 +317,20 @@ static void snid_done(struct ccw_device *cdev, int rc)
        else if (mismatch)
                rc = -EOPNOTSUPP;
        else {
-               sch->vpm = pgid_to_vpm(cdev);
+               donepm = pgid_to_donepm(cdev);
+               sch->vpm = donepm & sch->opm;
+               cdev->private->pgid_todo_mask &= ~donepm;
                pgid_fill(cdev, pgid);
        }
 out:
        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);
+                     "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid,
+                     id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
+                     cdev->private->pgid_todo_mask, mismatch, reserved, reset);
        switch (rc) {
        case 0:
                /* Anything left to do? */
-               if (sch->vpm == sch->schib.pmcw.pam) {
+               if (cdev->private->pgid_todo_mask == 0) {
                        verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
                        return;
                }
@@ -411,6 +414,7 @@ static void verify_start(struct ccw_device *cdev)
        struct ccw_dev_id *devid = &cdev->private->dev_id;
 
        sch->vpm = 0;
+       sch->lpm = sch->schib.pmcw.pam;
        /* Initialize request data. */
        memset(req, 0, sizeof(*req));
        req->timeout    = PGID_TIMEOUT;
@@ -442,11 +446,14 @@ static void verify_start(struct ccw_device *cdev)
  */
 void ccw_device_verify_start(struct ccw_device *cdev)
 {
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
        CIO_TRACE_EVENT(4, "vrfy");
        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->pgid_valid_mask = 0;
+       cdev->private->pgid_todo_mask = sch->schib.pmcw.pam;
        /*
         * Initialize pathgroup and multipath state with target values.
         * They may change in the course of path verification.
index d72ae4c..b9ce712 100644 (file)
@@ -150,6 +150,7 @@ struct ccw_device_private {
        struct ccw_request req;         /* internal I/O request */
        int iretry;
        u8 pgid_valid_mask;             /* mask of valid PGIDs */
+       u8 pgid_todo_mask;              /* mask of PGIDs to be adjusted */
        struct {
                unsigned int fast:1;    /* post with "channel end" */
                unsigned int repall:1;  /* report every interrupt status */