]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/s390/cio/device_pgid.c
Merge tag 'scsi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb...
[karo-tx-linux.git] / drivers / s390 / cio / device_pgid.c
index 908d287f66c13c1b1f41daf6bdb7b15bdd6d845f..37ada05e82a541241f47b460b58a3a4cd9d142ba 100644 (file)
@@ -23,6 +23,8 @@
 #define PGID_RETRIES   256
 #define PGID_TIMEOUT   (10 * HZ)
 
+static void verify_start(struct ccw_device *cdev);
+
 /*
  * Process path verification data and report result.
  */
@@ -70,8 +72,8 @@ static void nop_do(struct ccw_device *cdev)
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
        struct ccw_request *req = &cdev->private->req;
 
-       /* Adjust lpm. */
-       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm);
+       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm &
+                             ~cdev->private->path_noirq_mask);
        if (!req->lpm)
                goto out_nopath;
        nop_build_cp(cdev);
@@ -102,10 +104,20 @@ static void nop_callback(struct ccw_device *cdev, void *data, int rc)
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
        struct ccw_request *req = &cdev->private->req;
 
-       if (rc == 0)
+       switch (rc) {
+       case 0:
                sch->vpm |= req->lpm;
-       else if (rc != -EACCES)
+               break;
+       case -ETIME:
+               cdev->private->path_noirq_mask |= req->lpm;
+               break;
+       case -EACCES:
+               cdev->private->path_notoper_mask |= req->lpm;
+               break;
+       default:
                goto err;
+       }
+       /* Continue on the next path. */
        req->lpm >>= 1;
        nop_do(cdev);
        return;
@@ -132,6 +144,48 @@ static void spid_build_cp(struct ccw_device *cdev, u8 fn)
        req->cp         = cp;
 }
 
+static void pgid_wipeout_callback(struct ccw_device *cdev, void *data, int rc)
+{
+       if (rc) {
+               /* We don't know the path groups' state. Abort. */
+               verify_done(cdev, rc);
+               return;
+       }
+       /*
+        * Path groups have been reset. Restart path verification but
+        * leave paths in path_noirq_mask out.
+        */
+       cdev->private->flags.pgid_unknown = 0;
+       verify_start(cdev);
+}
+
+/*
+ * Reset pathgroups and restart path verification, leave unusable paths out.
+ */
+static void pgid_wipeout_start(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_dev_id *id = &cdev->private->dev_id;
+       struct ccw_request *req = &cdev->private->req;
+       u8 fn;
+
+       CIO_MSG_EVENT(2, "wipe: device 0.%x.%04x: pvm=%02x nim=%02x\n",
+                     id->ssid, id->devno, cdev->private->pgid_valid_mask,
+                     cdev->private->path_noirq_mask);
+
+       /* Initialize request data. */
+       memset(req, 0, sizeof(*req));
+       req->timeout    = PGID_TIMEOUT;
+       req->maxretries = PGID_RETRIES;
+       req->lpm        = sch->schib.pmcw.pam;
+       req->callback   = pgid_wipeout_callback;
+       fn = SPID_FUNC_DISBAND;
+       if (cdev->private->flags.mpath)
+               fn |= SPID_FUNC_MULTI_PATH;
+       spid_build_cp(cdev, fn);
+       ccw_request_start(cdev);
+}
+
 /*
  * Perform establish/resign SET PGID on a single path.
  */
@@ -157,11 +211,14 @@ static void spid_do(struct ccw_device *cdev)
        return;
 
 out_nopath:
+       if (cdev->private->flags.pgid_unknown) {
+               /* At least one SPID could be partially done. */
+               pgid_wipeout_start(cdev);
+               return;
+       }
        verify_done(cdev, sch->vpm ? 0 : -EACCES);
 }
 
-static void verify_start(struct ccw_device *cdev);
-
 /*
  * Process SET PGID request result for a single path.
  */
@@ -174,7 +231,12 @@ static void spid_callback(struct ccw_device *cdev, void *data, int rc)
        case 0:
                sch->vpm |= req->lpm & sch->opm;
                break;
+       case -ETIME:
+               cdev->private->flags.pgid_unknown = 1;
+               cdev->private->path_noirq_mask |= req->lpm;
+               break;
        case -EACCES:
+               cdev->private->path_notoper_mask |= req->lpm;
                break;
        case -EOPNOTSUPP:
                if (cdev->private->flags.mpath) {
@@ -330,8 +392,9 @@ static void snid_done(struct ccw_device *cdev, int rc)
        else {
                donepm = pgid_to_donepm(cdev);
                sch->vpm = donepm & sch->opm;
-               cdev->private->pgid_todo_mask &= ~donepm;
                cdev->private->pgid_reset_mask |= reset;
+               cdev->private->pgid_todo_mask &=
+                       ~(donepm | cdev->private->path_noirq_mask);
                pgid_fill(cdev, pgid);
        }
 out:
@@ -341,6 +404,10 @@ out:
                      cdev->private->pgid_todo_mask, mismatch, reserved, reset);
        switch (rc) {
        case 0:
+               if (cdev->private->flags.pgid_unknown) {
+                       pgid_wipeout_start(cdev);
+                       return;
+               }
                /* Anything left to do? */
                if (cdev->private->pgid_todo_mask == 0) {
                        verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
@@ -384,9 +451,10 @@ static void snid_do(struct ccw_device *cdev)
 {
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
        struct ccw_request *req = &cdev->private->req;
+       int ret;
 
-       /* Adjust lpm if paths are not set in pam. */
-       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam);
+       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam &
+                             ~cdev->private->path_noirq_mask);
        if (!req->lpm)
                goto out_nopath;
        snid_build_cp(cdev);
@@ -394,7 +462,13 @@ static void snid_do(struct ccw_device *cdev)
        return;
 
 out_nopath:
-       snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES);
+       if (cdev->private->pgid_valid_mask)
+               ret = 0;
+       else if (cdev->private->path_noirq_mask)
+               ret = -ETIME;
+       else
+               ret = -EACCES;
+       snid_done(cdev, ret);
 }
 
 /*
@@ -404,10 +478,21 @@ static void snid_callback(struct ccw_device *cdev, void *data, int rc)
 {
        struct ccw_request *req = &cdev->private->req;
 
-       if (rc == 0)
+       switch (rc) {
+       case 0:
                cdev->private->pgid_valid_mask |= req->lpm;
-       else if (rc != -EACCES)
+               break;
+       case -ETIME:
+               cdev->private->flags.pgid_unknown = 1;
+               cdev->private->path_noirq_mask |= req->lpm;
+               break;
+       case -EACCES:
+               cdev->private->path_notoper_mask |= req->lpm;
+               break;
+       default:
                goto err;
+       }
+       /* Continue on the next path. */
        req->lpm >>= 1;
        snid_do(cdev);
        return;
@@ -427,6 +512,13 @@ static void verify_start(struct ccw_device *cdev)
 
        sch->vpm = 0;
        sch->lpm = sch->schib.pmcw.pam;
+
+       /* 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;
+       cdev->private->path_notoper_mask = 0;
+
        /* Initialize request data. */
        memset(req, 0, sizeof(*req));
        req->timeout    = PGID_TIMEOUT;
@@ -459,14 +551,8 @@ 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.
@@ -474,6 +560,7 @@ void ccw_device_verify_start(struct ccw_device *cdev)
        cdev->private->flags.pgroup = cdev->private->options.pgroup;
        cdev->private->flags.mpath = cdev->private->options.mpath;
        cdev->private->flags.doverify = 0;
+       cdev->private->path_noirq_mask = 0;
        verify_start(cdev);
 }